From: Naveed Khan <naveed@digiscrypt.com>
To: bpf@vger.kernel.org
Subject: [PATCH] libbpf: bounds-check struct_ops member offset before writing shadow pointer
Date: Thu, 23 Jul 2026 12:15:30 +0530 [thread overview]
Message-ID: <178478913040.2.14225985431891987705@digiscrypt.com> (raw)
bpf_object__collect_st_ops_relos() replaces each function pointer in a
struct_ops value with a pointer to the corresponding struct bpf_program
in the map's shadow data:
*((struct bpf_program **)(st_ops->data + moff)) = prog;
st_ops->data is allocated with malloc(type->size), so it is exactly
map->def.value_size bytes long, and moff is the byte offset of the
member being relocated. The only bound placed on moff comes from
find_struct_ops_map_by_offset():
offset - map->sec_offset < map->def.value_size
so moff can be as large as value_size - 1. The store above is
sizeof(struct bpf_program *) bytes wide, and nothing checks that
moff + sizeof(struct bpf_program *) stays within value_size.
libbpf's BTF sanity check does not validate that a struct member's
offset plus its size fits within the struct's declared size (see
btf_validate_type()), and this relocation runs at bpf_object__open()
time, before the kernel validates the struct_ops type. A crafted object
with a struct_ops struct whose declared size is small (e.g. 1) but which
contains a function-pointer member near the end of (or beyond) that size
therefore makes libbpf write up to sizeof(void *) - 1 bytes past the heap
allocation - a controlled heap out-of-bounds write reachable purely from
opening an untrusted object file.
Reject such relocations by requiring the whole pointer store to fit
within the map value.
Signed-off-by: Naveed Khan <naveed@digiscrypt.com>
---
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 1368752aa1..1aa2a1d098 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -10529,6 +10529,17 @@ static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
return -EINVAL;
}
+ /* the shadow pointer stored below is sizeof(struct bpf_program *)
+ * bytes wide, so the whole write must fit within st_ops->data,
+ * which is only map->def.value_size bytes long. A malformed BTF
+ * can place a member near the end of the value and overflow it.
+ */
+ if (moff + sizeof(struct bpf_program *) > map->def.value_size) {
+ pr_warn("struct_ops reloc %s: member %s at moff %u overflows map value size %u\n",
+ map->name, name, moff, map->def.value_size);
+ return -EINVAL;
+ }
+
prog = find_prog_by_sec_insn(obj, shdr_idx, insn_idx);
if (!prog) {
pr_warn("struct_ops reloc %s: cannot find prog at shdr_idx %u to relocate func ptr %s\n",
--
2.52.0
next reply other threads:[~2026-07-23 6:45 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-23 6:45 Naveed Khan [this message]
2026-07-23 7:03 ` [PATCH] libbpf: bounds-check struct_ops member offset before writing shadow pointer sashiko-bot
2026-07-23 7:44 ` bot+bpf-ci
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=178478913040.2.14225985431891987705@digiscrypt.com \
--to=naveed@digiscrypt.com \
--cc=bpf@vger.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.