BPF List
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Henry Martin" <bsdhenrymartin@gmail.com>
Cc: bpf@vger.kernel.org
Subject: Re: [PATCH v2] bpf: fix out-of-bounds frame access for async callback stack args
Date: Tue, 27 Oct 2026 16:03:50 +0000	[thread overview]
Message-ID: <sashiko-outbox-186872@kernel.org> (raw)
In-Reply-To: <20261027154553.1043567-1-bsdhenrymartin@gmail.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] bpf: NULL or OOB read of stack_arg_regs in check_stack_arg_read()
--

commit 8bf4e754c411e80ff88d77c6c807380a3c5428eb
Author: Henry Martin <bsdhenrymartin@gmail.com>

bpf: fix out-of-bounds frame access for async callback stack args

This commit rejects r11-based incoming stack argument loads in
check_stack_arg_read() when the current frame has curframe == 0 or
state->in_callback_fn set. This prevents async and synchronous callback
frames from reading unvalidated or out-of-bounds caller frame and stack
argument state.

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 5601d20562186..c02776c396bc2 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -4653,6 +4653,11 @@ static int check_stack_arg_read(struct bpf_verifier_env *env, struct bpf_func_st
>  		return -EACCES;
>  	}
>  
> +	if (vstate->curframe == 0 || state->in_callback_fn) {
> +		verbose(env, "stack arg read from a callback frame\n");
> +		return -EINVAL;
> +	}
> +
>  	caller = vstate->frame[vstate->curframe - 1];
>  	arg = &caller->stack_arg_regs[spi];
>  	cur = vstate->frame[vstate->curframe];

[Severity: High]
Can check_stack_arg_read() in kernel/bpf/verifier.c still dereference a NULL
or out-of-bounds caller->stack_arg_regs[spi] (or copy an invalidated NOT_INIT
slot) during a non-callback static subprogram call?

For static subprograms, both check_func_call() and check_func_callx() ignore
all errors from btf_check_subprog_call() except -EFAULT and proceed into
check_static_func_call() with vstate->curframe >= 1 and
state->in_callback_fn == false:

kernel/bpf/verifier.c:check_func_call() {
	...
	caller = state->frame[state->curframe];
	err = btf_check_subprog_call(env, subprog);
	if (err == -EFAULT)
		return err;
	...
}

kernel/bpf/verifier.c:check_func_callx() {
	...
	subprog = reg->subprogno;
	err = btf_check_subprog_call(env, subprog);
	if (err == -EFAULT)
		return err;

	err = record_callx_edge(env, caller->subprogno, subprog);
	if (err)
		return err;

	return check_static_func_call(env, subprog, insn_idx);
}

When a static subprogram has more than five BTF argument slots
(sub->arg_slot_cnt > MAX_BPF_FUNC_REG_ARGS, so bpf_in_stack_arg_cnt(subprog)
is non-zero), btf_check_subprog_call() can return -EINVAL before
check_func_args() -> check_outgoing_stack_args() validates and prepares
caller->stack_arg_regs in two ways:

1. In btf_check_func_arg_match(), after btf_prepare_func_args() succeeds and
populates sub->arg_slot_cnt and sub->stack_arg_cnt, if any register in
caller->regs[BPF_REG_1 .. BPF_REG_5] has type == NOT_INIT, it returns
-EINVAL before calling check_func_args():

kernel/bpf/verifier.c:btf_check_func_arg_match() {
	...
	if (!bpf_subprog_is_global(env, subprog)) {
		for (i = 0; i < min_t(u32, sub->arg_slot_cnt, MAX_BPF_FUNC_REG_ARGS); i++)
			if (caller->regs[BPF_REG_1 + i].type == NOT_INIT)
				return -EINVAL;
	}
	return check_func_args(env, &meta, env->insn_idx);
}

2. In btf_check_subprog_call(), once
prog->aux->func_info_aux[subprog].unreliable is set to true by an earlier
call or callback registration that failed BTF validation, any subsequent call
to that static subprogram returns -EINVAL immediately without calling
btf_check_func_arg_match() or check_outgoing_stack_args():

kernel/bpf/verifier.c:btf_check_subprog_call() {
	...
	if (prog->aux->func_info_aux[subprog].unreliable)
		return -EINVAL;

	err = btf_check_func_arg_match(env, subprog, btf);
	if (err)
		prog->aux->func_info_aux[subprog].unreliable = true;
	return err;
}

When that static subprogram then executes an r11-based incoming stack
argument load, check_stack_arg_read() passes the spi + 1 <=
bpf_in_stack_arg_cnt(subprog) bound check and the new curframe == 0 ||
state->in_callback_fn check, and then reads caller->stack_arg_regs[spi].

If the caller never allocated stack_arg_regs (caller->out_stack_arg_cnt == 0),
this dereferences NULL; if caller->out_stack_arg_cnt <= spi, it reads past
the end of caller->stack_arg_regs; and if a prior call left invalidated slots
behind, it copies a NOT_INIT register state into cur->regs[dst_regno].

Should check_stack_arg_read() also verify that spi < caller->out_stack_arg_cnt
and that caller->stack_arg_regs[spi].type != NOT_INIT (or reject the call
when btf_check_subprog_call() fails for a subprogram with incoming stack
arguments)?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261027154553.1043567-1-bsdhenrymartin@gmail.com?part=1

      reply	other threads:[~2026-10-27 16:03 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-27 15:45 [PATCH v2] bpf: fix out-of-bounds frame access for async callback stack args Henry Martin
2026-10-27 16:03 ` sashiko-bot [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=sashiko-outbox-186872@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=bsdhenrymartin@gmail.com \
    --cc=sashiko-reviews@lists.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