bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
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;
> > +}
> > +
> 
> [...]

  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).