From: Paul Chaignon <paul.chaignon@gmail.com>
To: bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Quentin Monnet <qmo@kernel.org>
Subject: [PATCH bpf-next 3/8] bpf: Create inner oracle maps
Date: Mon, 8 Dec 2025 11:07:06 +0900 [thread overview]
Message-ID: <7229111ef814fd10cac9c3a14d38ddb0f39dc376.1765158925.git.paul.chaignon@gmail.com> (raw)
In-Reply-To: <cover.1765158924.git.paul.chaignon@gmail.com>
This patch creates the inner oracle maps that will store the information
on verifier states. Each pruning point needs an inner oracle map. They
are called inner because they will all be referred by a hashmap in a
later commit, indexed by instruction indexes. They are also referred
in the oracle instructions, for easier lookup from the interpreter.
For the inner maps, we can rely on array maps because at the time we
create them we know how many states will need to be saved. They won't
grow after program loading so they can have a static size.
These maps are not only useful for the oracle to iterate through states,
but also for debugging from userspace after we hit an oracle test
warning. Userspace should however never need to update them, so let's
limit permissions accordingly.
The bytecode ends up looking like:
0: (bf) r2 = r10
1: (7a) *(u64 *)(r2 -40) = -44
2: (79) r6 = *(u64 *)(r2 -40)
3: (85) call bpf_user_rnd_u32#28800
4: (18) r0 = oracle_map[id:21]
6: (b7) r0 = 0
7: (95) exit
with our special instruction at index 4. A subsequent patch teaches the
interpreter to skip this special instruction at runtime, to avoid
overwriting R0.
Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
---
include/linux/bpf.h | 3 ++
include/linux/bpf_verifier.h | 6 +++-
kernel/bpf/oracle.c | 64 +++++++++++++++++++++++++++++++++++-
kernel/bpf/syscall.c | 8 ++---
kernel/bpf/verifier.c | 2 +-
5 files changed, 76 insertions(+), 7 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 28d8d6b7bb1e..6bec31816485 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -617,6 +617,9 @@ void bpf_rb_root_free(const struct btf_field *field, void *rb_root,
u64 bpf_arena_get_kern_vm_start(struct bpf_arena *arena);
u64 bpf_arena_get_user_vm_start(struct bpf_arena *arena);
int bpf_obj_name_cpy(char *dst, const char *src, unsigned int size);
+int bpf_map_alloc_id(struct bpf_map *map);
+void bpf_map_save_memcg(struct bpf_map *map);
+void bpf_map_free(struct bpf_map *map);
struct bpf_offload_dev;
struct bpf_offloaded_map;
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index e4c8457e02c1..a93b5e2f4d7f 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -575,7 +575,10 @@ struct bpf_insn_aux_data {
};
struct bpf_iarray *jt; /* jump table for gotox or bpf_tailcall call instruction */
struct btf_struct_meta *kptr_struct_meta;
- struct list_head *oracle_states;
+ union {
+ struct list_head *oracle_states;
+ struct bpf_map *oracle_inner_map;
+ };
u64 map_key_state; /* constant (32 bit) key tracking for maps */
int ctx_field_size; /* the ctx field size for load insn, maybe 0 */
u32 seen; /* this insn was processed by the verifier at env->pass_cnt */
@@ -1109,6 +1112,7 @@ void bpf_fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask);
bool bpf_calls_callback(struct bpf_verifier_env *env, int insn_idx);
struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
const struct bpf_insn *patch, u32 len);
+int __add_used_map(struct bpf_verifier_env *env, struct bpf_map *map);
int bpf_stack_liveness_init(struct bpf_verifier_env *env);
void bpf_stack_liveness_free(struct bpf_verifier_env *env);
diff --git a/kernel/bpf/oracle.c b/kernel/bpf/oracle.c
index 924a86c90b4e..66ee840a35eb 100644
--- a/kernel/bpf/oracle.c
+++ b/kernel/bpf/oracle.c
@@ -62,6 +62,53 @@ int save_state_in_oracle(struct bpf_verifier_env *env, int insn_idx)
return 0;
}
+static struct bpf_map *create_inner_oracle_map(size_t size)
+{
+ struct bpf_map *map;
+ int err;
+
+ union bpf_attr map_attr = {
+ .map_type = BPF_MAP_TYPE_ARRAY,
+ .key_size = sizeof(__u32),
+ .value_size = sizeof(struct bpf_oracle_state),
+ .max_entries = size,
+ .map_flags = BPF_F_INNER_MAP | BPF_F_RDONLY,
+ .map_name = "oracle_inner",
+ };
+ map = array_map_ops.map_alloc(&map_attr);
+ if (IS_ERR(map))
+ return map;
+
+ map->ops = &array_map_ops;
+ map->map_type = BPF_MAP_TYPE_ARRAY;
+
+ err = bpf_obj_name_cpy(map->name, map_attr.map_name,
+ sizeof(map_attr.map_name));
+ if (err < 0)
+ goto free_map;
+
+ mutex_init(&map->freeze_mutex);
+ spin_lock_init(&map->owner_lock);
+
+ err = security_bpf_map_create(map, &map_attr, NULL, false);
+ if (err)
+ goto free_map_sec;
+
+ err = bpf_map_alloc_id(map);
+ if (err)
+ goto free_map_sec;
+
+ bpf_map_save_memcg(map);
+
+ return map;
+
+free_map_sec:
+ security_bpf_map_free(map);
+free_map:
+ bpf_map_free(map);
+ return ERR_PTR(err);
+}
+
struct bpf_prog *patch_oracle_check_insn(struct bpf_verifier_env *env, struct bpf_insn *insn,
int i, int *cnt)
{
@@ -72,7 +119,8 @@ struct bpf_prog *patch_oracle_check_insn(struct bpf_verifier_env *env, struct bp
struct list_head *head = aux->oracle_states;
struct bpf_insn *insn_buf = env->insn_buf;
struct bpf_prog *new_prog = env->prog;
- int num_oracle_states;
+ int num_oracle_states, err;
+ struct bpf_map *inner_map;
if (env->subprog_cnt > 1)
/* Skip the oracle if subprogs are used. */
@@ -82,6 +130,12 @@ struct bpf_prog *patch_oracle_check_insn(struct bpf_verifier_env *env, struct bp
if (!num_oracle_states)
goto noop;
+ inner_map = create_inner_oracle_map(num_oracle_states);
+ if (IS_ERR(inner_map))
+ return (void *)inner_map;
+
+ ld_addrs[0].imm = (u32)(u64)inner_map;
+ ld_addrs[1].imm = ((u64)inner_map) >> 32;
insn_buf[0] = ld_addrs[0];
insn_buf[1] = ld_addrs[1];
insn_buf[2] = *insn;
@@ -91,6 +145,14 @@ struct bpf_prog *patch_oracle_check_insn(struct bpf_verifier_env *env, struct bp
if (!new_prog)
return ERR_PTR(-ENOMEM);
+ /* Attach oracle inner map to new LDIMM64 instruction. */
+ aux = &env->insn_aux_data[i];
+ aux->oracle_inner_map = inner_map;
+
+ err = __add_used_map(env, inner_map);
+ if (err < 0)
+ return ERR_PTR(err);
+
return new_prog;
noop:
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 211912c91652..5d8db1fed082 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -441,7 +441,7 @@ void bpf_map_init_from_attr(struct bpf_map *map, union bpf_attr *attr)
map->map_extra = attr->map_extra;
}
-static int bpf_map_alloc_id(struct bpf_map *map)
+int bpf_map_alloc_id(struct bpf_map *map)
{
int id;
@@ -480,7 +480,7 @@ void bpf_map_free_id(struct bpf_map *map)
}
#ifdef CONFIG_MEMCG
-static void bpf_map_save_memcg(struct bpf_map *map)
+void bpf_map_save_memcg(struct bpf_map *map)
{
/* Currently if a map is created by a process belonging to the root
* memory cgroup, get_obj_cgroup_from_current() will return NULL.
@@ -580,7 +580,7 @@ void __percpu *bpf_map_alloc_percpu(const struct bpf_map *map, size_t size,
}
#else
-static void bpf_map_save_memcg(struct bpf_map *map)
+void bpf_map_save_memcg(struct bpf_map *map)
{
}
@@ -880,7 +880,7 @@ void bpf_obj_free_fields(const struct btf_record *rec, void *obj)
}
}
-static void bpf_map_free(struct bpf_map *map)
+void bpf_map_free(struct bpf_map *map)
{
struct btf_record *rec = map->record;
struct btf *btf = map->btf;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 4ca52c6aaa3b..74fc568c1bc8 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -20969,7 +20969,7 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
return 0;
}
-static int __add_used_map(struct bpf_verifier_env *env, struct bpf_map *map)
+int __add_used_map(struct bpf_verifier_env *env, struct bpf_map *map)
{
int i, err;
--
2.43.0
next prev parent reply other threads:[~2025-12-08 2:07 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-08 2:00 [PATCH bpf-next 0/8] bpf: Introduce verifier test oracle Paul Chaignon
2025-12-08 1:56 ` [PATCH bpf-next 1/8] bpf: Save pruning point states in oracle Paul Chaignon
2025-12-08 2:17 ` bot+bpf-ci
2025-12-08 2:02 ` [PATCH bpf-next 2/8] bpf: Patch bytecode with oracle check instructions Paul Chaignon
2025-12-08 2:07 ` Paul Chaignon [this message]
2025-12-09 2:23 ` [PATCH bpf-next 3/8] bpf: Create inner oracle maps kernel test robot
2025-12-09 16:20 ` kernel test robot
2025-12-08 2:08 ` [PATCH bpf-next 4/8] bpf: Populate " Paul Chaignon
2025-12-08 2:08 ` [PATCH bpf-next 5/8] bpf: Create and populate oracle map Paul Chaignon
2025-12-08 2:09 ` [PATCH bpf-next 6/8] bpf: Check oracle in interpreter Paul Chaignon
2025-12-09 3:24 ` kernel test robot
2025-12-09 13:41 ` kernel test robot
2025-12-08 2:09 ` [PATCH bpf-next 7/8] bpf: Introduce CONFIG_BPF_ORACLE Paul Chaignon
2025-12-08 2:09 ` [PATCH bpf-next 8/8] bpftool: Dump oracle maps Paul Chaignon
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=7229111ef814fd10cac9c3a14d38ddb0f39dc376.1765158925.git.paul.chaignon@gmail.com \
--to=paul.chaignon@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=qmo@kernel.org \
/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).