From: Slava Imameev <slava.imameev@crowdstrike.com>
To: <sast@kernel.org>, <daniel@iogearbox.net>, <andrii@kernel.org>
Cc: <sartin.lau@linux.dev>, <eddyz87@gmail.com>, <song@kernel.org>,
<yonghong.song@linux.dev>, <john.fastabend@gmail.com>,
<kpsingh@kernel.org>, <sdf@fomichev.me>, <haoluo@google.com>,
<jolsa@kernel.org>, <davem@davemloft.net>, <edumazet@google.com>,
<kuba@kernel.org>, <pabeni@redhat.com>, <horms@kernel.org>,
<shuah@kernel.org>, <linux-kernel@vger.kernel.org>,
<bpf@vger.kernel.org>, <netdev@vger.kernel.org>,
<linux-kselftest@vger.kernel.org>,
<linux-open-source@crowdstrike.com>,
Slava Imameev <slava.imameev@crowdstrike.com>
Subject: [PATCH bpf-next 1/2] bpf: Support multi-level pointer params via PTR_TO_MEM for trampolines
Date: Wed, 18 Feb 2026 08:45:32 +1100 [thread overview]
Message-ID: <20260217214533.17776-2-slava.imameev@crowdstrike.com> (raw)
In-Reply-To: <20260217214533.17776-1-slava.imameev@crowdstrike.com>
Add BPF verifier support for multi-level pointer parameters and return
values in BPF trampolines. The implementation treats these parameters as
PTR_TO_MEM with read-only semantics, applying either untrusted or trusted
access patterns while honoring __nullable annotations. Runtime safety is
ensured through existing exception handling mechanisms for untrusted
memory reads, with the verifier enforcing bounds checking and null
validation.
Signed-off-by: Slava Imameev <slava.imameev@crowdstrike.com>
---
include/linux/bpf.h | 3 ++-
kernel/bpf/btf.c | 54 ++++++++++++++++++++++++++++++++++++-------
kernel/bpf/verifier.c | 4 +++-
3 files changed, 51 insertions(+), 10 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index cd9b96434904..6dd6a85cf13a 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1052,7 +1052,8 @@ struct bpf_insn_access_aux {
struct btf *btf;
u32 btf_id;
u32 ref_obj_id;
- };
+ }; /* base type PTR_TO_BTF_ID */
+ u32 mem_size; /* base type PTR_TO_MEM */
};
struct bpf_verifier_log *log; /* for verbose logs */
bool is_retval; /* is accessing function return value ? */
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 7708958e3fb8..7b7cb30cdc98 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -760,6 +760,21 @@ const struct btf_type *btf_type_resolve_func_ptr(const struct btf *btf,
return NULL;
}
+static bool is_multilevel_ptr(const struct btf *btf, const struct btf_type *t)
+{
+ u32 depth = 0;
+
+ if (!btf_type_is_ptr(t))
+ return false;
+
+ do {
+ depth += 1;
+ t = btf_type_skip_modifiers(btf, t->type, NULL);
+ } while (btf_type_is_ptr(t) && depth < 2);
+
+ return depth > 1;
+}
+
/* Types that act only as a source, not sink or intermediate
* type when resolving.
*/
@@ -6790,6 +6805,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
const char *tag_value;
u32 nr_args, arg;
int i, ret;
+ bool trusted, nullable;
if (off % 8) {
bpf_log(log, "func '%s' offset %d is not multiple of 8\n",
@@ -6927,12 +6943,8 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
}
}
- info->reg_type = PTR_TO_BTF_ID;
- if (prog_args_trusted(prog))
- info->reg_type |= PTR_TRUSTED;
-
- if (btf_param_match_suffix(btf, &args[arg], "__nullable"))
- info->reg_type |= PTR_MAYBE_NULL;
+ trusted = prog_args_trusted(prog);
+ nullable = btf_param_match_suffix(btf, &args[arg], "__nullable");
if (prog->expected_attach_type == BPF_TRACE_RAW_TP) {
struct btf *btf = prog->aux->attach_btf;
@@ -6953,7 +6965,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
if (strcmp(tname, raw_tp_null_args[i].func))
continue;
if (raw_tp_null_args[i].mask & (0x1ULL << (arg * 4)))
- info->reg_type |= PTR_MAYBE_NULL;
+ nullable = true;
/* Is the current arg IS_ERR? */
if (raw_tp_null_args[i].mask & (0x2ULL << (arg * 4)))
ptr_err_raw_tp = true;
@@ -6964,9 +6976,35 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
* argument as PTR_MAYBE_NULL.
*/
if (i == ARRAY_SIZE(raw_tp_null_args) && btf_is_module(btf))
- info->reg_type |= PTR_MAYBE_NULL;
+ nullable = true;
}
+ if (is_multilevel_ptr(btf, t)) {
+ /* If it can be IS_ERR at runtime, mark as scalar. */
+ if (ptr_err_raw_tp) {
+ bpf_log(log, "marking func '%s' pointer arg%d as scalar as it may encode error",
+ tname, arg);
+ info->reg_type = SCALAR_VALUE;
+ } else {
+ info->reg_type = PTR_TO_MEM | MEM_RDONLY;
+ if (!trusted)
+ info->reg_type |= PTR_UNTRUSTED;
+ /* for return value be conservative and mark it nullable */
+ if (nullable || arg == nr_args)
+ info->reg_type |= PTR_MAYBE_NULL;
+ /* this is a pointer to another pointer */
+ info->mem_size = sizeof(void *);
+ }
+ return true;
+ }
+
+ info->reg_type = PTR_TO_BTF_ID;
+ if (trusted)
+ info->reg_type |= PTR_TRUSTED;
+
+ if (nullable)
+ info->reg_type |= PTR_MAYBE_NULL;
+
if (tgt_prog) {
enum bpf_prog_type tgt_type;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 0162f946032f..5de56336e169 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6311,7 +6311,7 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,
off);
return -EACCES;
}
- } else {
+ } else if (base_type(info->reg_type) != PTR_TO_MEM) {
env->insn_aux_data[insn_idx].ctx_field_size = info->ctx_field_size;
}
/* remember the offset of last byte accessed in ctx */
@@ -7771,6 +7771,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
regs[value_regno].btf = info.btf;
regs[value_regno].btf_id = info.btf_id;
regs[value_regno].ref_obj_id = info.ref_obj_id;
+ } else if (base_type(info.reg_type) == PTR_TO_MEM) {
+ regs[value_regno].mem_size = info.mem_size;
}
}
regs[value_regno].type = info.reg_type;
--
2.50.1 (Apple Git-155)
next prev parent reply other threads:[~2026-02-17 21:57 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-17 21:45 [PATCH bpf-next 0/2] bpf: Add multi-level pointer parameter support for trampolines Slava Imameev
2026-02-17 21:45 ` Slava Imameev [this message]
2026-02-17 21:45 ` [PATCH bpf-next 2/2] selftests/bpf: Add trampolines multi-level pointer params test coverage Slava Imameev
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=20260217214533.17776-2-slava.imameev@crowdstrike.com \
--to=slava.imameev@crowdstrike.com \
--cc=andrii@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=eddyz87@gmail.com \
--cc=edumazet@google.com \
--cc=haoluo@google.com \
--cc=horms@kernel.org \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=kpsingh@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-open-source@crowdstrike.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sartin.lau@linux.dev \
--cc=sast@kernel.org \
--cc=sdf@fomichev.me \
--cc=shuah@kernel.org \
--cc=song@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