From: Anton Protopopov <a.s.protopopov@gmail.com>
To: Eduard Zingerman <eddyz87@gmail.com>
Cc: bpf@vger.kernel.org, Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Anton Protopopov <aspsk@isovalent.com>,
Daniel Borkmann <daniel@iogearbox.net>,
Quentin Monnet <qmo@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>
Subject: Re: [PATCH v6 bpf-next 10/17] bpf, x86: add support for indirect jumps
Date: Wed, 22 Oct 2025 06:51:32 +0000 [thread overview]
Message-ID: <aPh+9Lw+vmD1nXqY@mail.gmail.com> (raw)
In-Reply-To: <c3de352f15a5004c48f4b37bfb4294f6602ec644.camel@gmail.com>
On 25/10/21 02:17PM, Eduard Zingerman wrote:
> On Sun, 2025-10-19 at 20:21 +0000, Anton Protopopov wrote:
> > Add support for a new instruction
> >
> > BPF_JMP|BPF_X|BPF_JA, SRC=0, DST=Rx, off=0, imm=0
> >
> > which does an indirect jump to a location stored in Rx. The register
> > Rx should have type PTR_TO_INSN. This new type assures that the Rx
> > register contains a value (or a range of values) loaded from a
> > correct jump table – map of type instruction array.
> >
> > For example, for a C switch LLVM will generate the following code:
> >
> > 0: r3 = r1 # "switch (r3)"
> > 1: if r3 > 0x13 goto +0x666 # check r3 boundaries
> > 2: r3 <<= 0x3 # adjust to an index in array of addresses
> > 3: r1 = 0xbeef ll # r1 is PTR_TO_MAP_VALUE, r1->map_ptr=M
> > 5: r1 += r3 # r1 inherits boundaries from r3
> > 6: r1 = *(u64 *)(r1 + 0x0) # r1 now has type INSN_TO_PTR
> > 7: gotox r1 # jit will generate proper code
> >
> > Here the gotox instruction corresponds to one particular map. This is
> > possible however to have a gotox instruction which can be loaded from
> > different maps, e.g.
> >
> > 0: r1 &= 0x1
> > 1: r2 <<= 0x3
> > 2: r3 = 0x0 ll # load from map M_1
> > 4: r3 += r2
> > 5: if r1 == 0x0 goto +0x4
> > 6: r1 <<= 0x3
> > 7: r3 = 0x0 ll # load from map M_2
> > 9: r3 += r1
> > A: r1 = *(u64 *)(r3 + 0x0)
> > B: gotox r1 # jump to target loaded from M_1 or M_2
> >
> > During check_cfg stage the verifier will collect all the maps which
> > point to inside the subprog being verified. When building the config,
> > the high 16 bytes of the insn_state are used, so this patch
> > (theoretically) supports jump tables of up to 2^16 slots.
> >
> > During the later stage, in check_indirect_jump, it is checked that
> > the register Rx was loaded from a particular instruction array.
> >
> > Signed-off-by: Anton Protopopov <a.s.protopopov@gmail.com>
> > ---
>
> LGTM, please, address a few remaining points.
>
> Acked-by: Eduard Zingerman <eddyz87@gmail.com>
>
> [...]
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index ae017c032944..d2df21fde118 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
>
> [...]
>
> > +static struct bpf_iarray *
> > +create_jt(int t, struct bpf_verifier_env *env, int fd)
> ^^^^^^
> This parameter is unused
Ah, yes, no more ->imm. Fixed.
> > +{
> > + static struct bpf_subprog_info *subprog;
> > + int subprog_start, subprog_end;
> > + struct bpf_iarray *jt;
> > + int i;
> > +
> > + subprog = bpf_find_containing_subprog(env, t);
> > + subprog_start = subprog->start;
> > + subprog_end = (subprog + 1)->start;
> > + jt = jt_from_subprog(env, subprog_start, subprog_end);
> > + if (IS_ERR(jt))
> > + return jt;
> > +
> > + /* Check that the every element of the jump table fits within the given subprogram */
> > + for (i = 0; i < jt->cnt; i++) {
> > + if (jt->items[i] < subprog_start || jt->items[i] >= subprog_end) {
> > + verbose(env, "jump table for insn %d points outside of the subprog [%u,%u]",
> > + t, subprog_start, subprog_end);
> > + return ERR_PTR(-EINVAL);
> > + }
> > + }
> > +
> > + return jt;
> > +}
>
> [...]
>
> > +/* gotox *dst_reg */
> > +static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *insn)
> > +{
> > + struct bpf_verifier_state *other_branch;
> > + struct bpf_reg_state *dst_reg;
> > + struct bpf_map *map;
> > + u32 min_index, max_index;
> > + int err = 0;
> > + int n;
> > + int i;
> > +
> > + dst_reg = reg_state(env, insn->dst_reg);
> > + if (dst_reg->type != PTR_TO_INSN) {
> > + verbose(env, "R%d has type %d, expected PTR_TO_INSN\n",
> > + insn->dst_reg, dst_reg->type);
> > + return -EINVAL;
> > + }
> > +
> > + map = dst_reg->map_ptr;
> > + if (verifier_bug_if(!map, env, "R%d has an empty map pointer", insn->dst_reg))
> > + return -EFAULT;
> > +
> > + if (verifier_bug_if(map->map_type != BPF_MAP_TYPE_INSN_ARRAY, env,
> > + "R%d has incorrect map type %d", insn->dst_reg, map->map_type))
> > + return -EFAULT;
>
> Nit: we discussed this in v5, let's drop the verifier_bug_if() and
> return -EINVAL?
So, I think this is a verifier bug. We've checked above that the
register is PTR_TO_INSN, so it must have map and map type should
be BPF_MAP_TYPE_INSN_ARRAY. Now this is always true, for future
I added these warnings, just in case. Wdyt?
> > The program can be written in a way, such that e.g. hash map
> > pointer is passed as a parameter for gotox, that would be an
> > incorrect program, not a verifier bug.
>
> Also, use reg_type_str() instead of "type %d"?
This is map type, not register? Ah, you maybe meant the first
message, I will fix it, thanks.
> > +
> > + err = indirect_jump_min_max_index(env, insn->dst_reg, map, &min_index, &max_index);
> > + if (err)
> > + return err;
> > +
> > + /* Ensure that the buffer is large enough */
> > + if (!env->gotox_tmp_buf || env->gotox_tmp_buf->cnt < max_index - min_index + 1) {
> > + env->gotox_tmp_buf = iarray_realloc(env->gotox_tmp_buf,
> > + max_index - min_index + 1);
> > + if (!env->gotox_tmp_buf)
> > + return -ENOMEM;
> > + }
> > +
> > + n = copy_insn_array_uniq(map, min_index, max_index, env->gotox_tmp_buf->items);
>
> Nit: let's not forget about a follow-up to remove this allocation.
Thanks, in the list of followups.
> > + if (n < 0)
> > + return n;
> > + if (n == 0) {
> > + verbose(env, "register R%d doesn't point to any offset in map id=%d\n",
> > + insn->dst_reg, map->id);
> > + return -EINVAL;
> > + }
> > +
> > + for (i = 0; i < n - 1; i++) {
> > + other_branch = push_stack(env, env->gotox_tmp_buf->items[i],
> > + env->insn_idx, env->cur_state->speculative);
> > + if (IS_ERR(other_branch))
> > + return PTR_ERR(other_branch);
> > + }
> > + env->insn_idx = env->gotox_tmp_buf->items[n-1];
> > + return 0;
> > +}
> > +
>
> [...]
next prev parent reply other threads:[~2025-10-22 6:44 UTC|newest]
Thread overview: 53+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-19 20:21 [PATCH v6 bpf-next 00/17] BPF indirect jumps Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 01/17] bpf: fix the return value of push_stack Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 02/17] bpf: save the start of functions in bpf_prog_aux Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 03/17] bpf: generalize and export map_get_next_key for arrays Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 04/17] bpf, x86: add new map type: instructions array Anton Protopopov
2025-10-21 17:49 ` Alexei Starovoitov
2025-10-21 18:32 ` Anton Protopopov
2025-10-21 23:26 ` Eduard Zingerman
2025-10-24 12:12 ` Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 05/17] selftests/bpf: add selftests for new insn_array map Anton Protopopov
2025-10-21 23:51 ` Eduard Zingerman
2025-10-22 13:44 ` Anton Protopopov
2025-10-22 13:55 ` Eduard Zingerman
2025-10-22 14:06 ` Anton Protopopov
2025-10-22 14:03 ` Eduard Zingerman
2025-10-22 14:13 ` Anton Protopopov
2025-10-22 17:00 ` Eduard Zingerman
2025-10-19 20:21 ` [PATCH v6 bpf-next 06/17] bpf: support instructions arrays with constants blinding Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 07/17] selftests/bpf: test instructions arrays with blinding Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 08/17] bpf, x86: allow indirect jumps to r8...r15 Anton Protopopov
2025-10-20 8:38 ` Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 09/17] bpf: make bpf_insn_successors to return a pointer Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 10/17] bpf, x86: add support for indirect jumps Anton Protopopov
2025-10-20 7:23 ` Anton Protopopov
2025-10-21 21:17 ` Eduard Zingerman
2025-10-22 6:51 ` Anton Protopopov [this message]
2025-10-22 6:53 ` Eduard Zingerman
2025-10-19 20:21 ` [PATCH v6 bpf-next 11/17] bpf: disasm: add support for BPF_JMP|BPF_JA|BPF_X Anton Protopopov
2025-10-21 21:19 ` Eduard Zingerman
2025-10-19 20:21 ` [PATCH v6 bpf-next 12/17] bpf, docs: do not state that indirect jumps are not supported Anton Protopopov
2025-10-21 19:15 ` Eduard Zingerman
2025-10-21 19:32 ` Anton Protopopov
2025-10-21 19:36 ` Eduard Zingerman
2025-10-21 19:50 ` Anton Protopopov
2025-10-21 20:17 ` Eduard Zingerman
2025-10-19 20:21 ` [PATCH v6 bpf-next 13/17] libbpf: fix formatting of bpf_object__append_subprog_code Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 14/17] libbpf: support llvm-generated indirect jumps Anton Protopopov
2025-10-21 22:18 ` Eduard Zingerman
2025-10-21 22:45 ` Eduard Zingerman
2025-10-24 12:52 ` Anton Protopopov
2025-10-25 17:39 ` Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 15/17] bpftool: Recognize insn_array map type Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 16/17] selftests/bpf: add new verifier_gotox test Anton Protopopov
2025-10-21 22:42 ` Eduard Zingerman
2025-10-24 11:40 ` Anton Protopopov
2025-10-26 12:34 ` Anton Protopopov
2025-10-27 23:11 ` Eduard Zingerman
2025-10-28 12:06 ` Anton Protopopov
2025-10-19 20:21 ` [PATCH v6 bpf-next 17/17] selftests/bpf: add C-level selftests for indirect jumps Anton Protopopov
2025-10-22 0:27 ` Eduard Zingerman
2025-10-22 13:34 ` Anton Protopopov
2025-10-25 17:41 ` Anton Protopopov
2025-10-21 18:30 ` [PATCH v6 bpf-next 00/17] BPF " patchwork-bot+netdevbpf
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=aPh+9Lw+vmD1nXqY@mail.gmail.com \
--to=a.s.protopopov@gmail.com \
--cc=andrii@kernel.org \
--cc=aspsk@isovalent.com \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=qmo@kernel.org \
--cc=yonghong.song@linux.dev \
/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;
as well as URLs for NNTP newsgroup(s).