From: Mingpei CAO <caomingpei@gmail.com>
To: bpf@vger.kernel.org
Cc: andrii@kernel.org, eddyz87@gmail.com, ameryhung@gmail.com,
qmo@kernel.org, Mingpei CAO <caomingpei@gmail.com>
Subject: [PATCH bpf v2 1/3] libbpf: Validate struct_ops member offsets before data access
Date: Sun, 13 Sep 2026 14:52:31 +0000 [thread overview]
Message-ID: <20260913145233.1446643-2-caomingpei@gmail.com> (raw)
In-Reply-To: <20260913145233.1446643-1-caomingpei@gmail.com>
A local struct_ops type can contain a bitfield absent from the
corresponding kernel BTF type. libbpf checks whether data for an absent
local member is zero.
For a bitfield, member->offset contains the bit position and field width.
bpf_map__init_kern_struct_ops() used the full value to create a data
pointer before checking the member range. libbpf_is_mem_zeroed() could
then read outside the local struct_ops data.
bpf_map__init_kern_struct_ops() also failed to check offsets for
non-bitfield members.
Reject local and kernel bitfields before reading member data. Check every
local and kernel member range before creating a data pointer.
Fixes: c911fc61a7ce ("libbpf: Skip zeroed or null fields if not found in the kernel type.")
Assisted-by: LLM
Signed-off-by: Mingpei CAO <caomingpei@gmail.com>
---
tools/lib/bpf/libbpf.c | 80 +++++++++++++++++++++++++++++++++++-------
1 file changed, 68 insertions(+), 12 deletions(-)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index b749c01742ee0..2791b48585939 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1175,6 +1175,7 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
const struct btf_member *member, *kern_member, *kern_data_member;
const struct btf_type *type, *kern_type, *kern_vtype;
__u32 i, kern_type_id, kern_vtype_id, kern_data_off;
+ __u32 kern_data_bit_off, kern_data_member_idx;
struct bpf_object *obj = map->obj;
const struct btf *btf = obj->btf;
struct bpf_struct_ops *st_ops;
@@ -1195,6 +1196,25 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
return err;
kern_btf = mod_btf ? mod_btf->btf : obj->btf_vmlinux;
+ kern_data_member_idx = kern_data_member - btf_members(kern_vtype);
+ if (btf_member_bitfield_size(kern_vtype, kern_data_member_idx)) {
+ pr_warn("struct_ops init_kern %s: kernel data member is a bitfield\n",
+ map->name);
+ return -ENOTSUP;
+ }
+ kern_data_bit_off = btf_member_bit_offset(kern_vtype, kern_data_member_idx);
+ if (kern_data_bit_off % 8) {
+ pr_warn("struct_ops init_kern %s: kernel data member has a non-byte-aligned offset\n",
+ map->name);
+ return -EINVAL;
+ }
+ kern_data_off = kern_data_bit_off / 8;
+ if (kern_data_off > kern_vtype->size ||
+ kern_type->size > kern_vtype->size - kern_data_off) {
+ pr_warn("struct_ops init_kern %s: kernel data member is outside the %u-byte kernel value type\n",
+ map->name, kern_vtype->size);
+ return -EINVAL;
+ }
pr_debug("struct_ops init_kern %s: type_id:%u kern_type_id:%u kern_vtype_id:%u\n",
map->name, st_ops->type_id, kern_type_id, kern_vtype_id);
@@ -1208,29 +1228,50 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
return -ENOMEM;
data = st_ops->data;
- kern_data_off = kern_data_member->offset / 8;
kern_data = st_ops->kern_vdata + kern_data_off;
member = btf_members(type);
for (i = 0; i < btf_vlen(type); i++, member++) {
const struct btf_type *mtype, *kern_mtype;
- __u32 mtype_id, kern_mtype_id;
+ __u32 bit_offset, mtype_id, kern_mtype_id;
void *mdata, *kern_mdata;
struct bpf_program *prog;
__s64 msize, kern_msize;
+ __u64 maccess_size;
__u32 moff, kern_moff;
__u32 kern_member_idx;
const char *mname;
mname = btf__name_by_offset(btf, member->name_off);
- moff = member->offset / 8;
- mdata = data + moff;
+ if (btf_member_bitfield_size(type, i)) {
+ pr_warn("struct_ops init_kern %s: local bitfield %s is not supported\n",
+ map->name, mname);
+ return -ENOTSUP;
+ }
+
msize = btf__resolve_size(btf, member->type);
if (msize < 0) {
pr_warn("struct_ops init_kern %s: failed to resolve the size of member %s\n",
map->name, mname);
return msize;
}
+ mtype = skip_mods_and_typedefs(btf, member->type, &mtype_id);
+ maccess_size = msize;
+ if (btf_is_ptr(mtype) && maccess_size < sizeof(prog))
+ maccess_size = sizeof(prog);
+ bit_offset = btf_member_bit_offset(type, i);
+ if (bit_offset % 8) {
+ pr_warn("struct_ops init_kern %s: member %s has a non-byte-aligned offset\n",
+ map->name, mname);
+ return -EINVAL;
+ }
+ moff = bit_offset / 8;
+ if (moff > type->size || maccess_size > type->size - moff) {
+ pr_warn("struct_ops init_kern %s: member %s is outside the %u-byte local struct_ops type\n",
+ map->name, mname, type->size);
+ return -EINVAL;
+ }
+ mdata = data + moff;
kern_member = find_member_by_name(kern_btf, kern_type, mname);
if (!kern_member) {
@@ -1259,17 +1300,33 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
}
kern_member_idx = kern_member - btf_members(kern_type);
- if (btf_member_bitfield_size(type, i) ||
- btf_member_bitfield_size(kern_type, kern_member_idx)) {
- pr_warn("struct_ops init_kern %s: bitfield %s is not supported\n",
+ if (btf_member_bitfield_size(kern_type, kern_member_idx)) {
+ pr_warn("struct_ops init_kern %s: kernel bitfield %s is not supported\n",
map->name, mname);
return -ENOTSUP;
}
- kern_moff = kern_member->offset / 8;
+ kern_msize = btf__resolve_size(kern_btf, kern_member->type);
+ if (kern_msize < 0) {
+ pr_warn("struct_ops init_kern %s: failed to resolve the kernel size of member %s\n",
+ map->name, mname);
+ return kern_msize;
+ }
+ bit_offset = btf_member_bit_offset(kern_type, kern_member_idx);
+ if (bit_offset % 8) {
+ pr_warn("struct_ops init_kern %s: kernel member %s has a non-byte-aligned offset\n",
+ map->name, mname);
+ return -EINVAL;
+ }
+ kern_moff = bit_offset / 8;
+ if (kern_moff > kern_type->size ||
+ (__u64)kern_msize > kern_type->size - kern_moff) {
+ pr_warn("struct_ops init_kern %s: kernel member %s is outside the %u-byte kernel struct_ops type\n",
+ map->name, mname, kern_type->size);
+ return -EINVAL;
+ }
kern_mdata = kern_data + kern_moff;
- mtype = skip_mods_and_typedefs(btf, member->type, &mtype_id);
kern_mtype = skip_mods_and_typedefs(kern_btf, kern_member->type,
&kern_mtype_id);
if (BTF_INFO_KIND(mtype->info) !=
@@ -1281,7 +1338,7 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
}
if (btf_is_ptr(mtype)) {
- prog = *(void **)mdata;
+ memcpy(&prog, mdata, sizeof(prog));
/* just like for !kern_member case above, reset declaratively
* set (at compile time) program's autload to false,
* if user replaced it with another program or NULL
@@ -1352,8 +1409,7 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
continue;
}
- kern_msize = btf__resolve_size(kern_btf, kern_mtype_id);
- if (kern_msize < 0 || msize != kern_msize) {
+ if (msize != kern_msize) {
pr_warn("struct_ops init_kern %s: Error in size of member %s: %zd != %zd(kernel)\n",
map->name, mname, (ssize_t)msize,
(ssize_t)kern_msize);
--
2.43.0
next prev parent reply other threads:[~2026-09-13 14:52 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-13 14:52 [PATCH bpf v2 0/3] libbpf: Validate struct_ops member offsets Mingpei CAO
2026-09-13 14:52 ` Mingpei CAO [this message]
2026-09-13 15:49 ` [PATCH bpf v2 1/3] libbpf: Validate struct_ops member offsets before data access bot+bpf-ci
2026-09-13 14:52 ` [PATCH bpf v2 2/3] bpftool: Keep struct_ops field offsets when skipping bitfields Mingpei CAO
2026-09-13 15:02 ` sashiko-bot
2026-09-13 15:49 ` bot+bpf-ci
2026-09-13 14:52 ` [PATCH bpf v2 3/3] selftests/bpf: Cover struct_ops bitfield and offset validation Mingpei CAO
2026-09-13 15:03 ` sashiko-bot
2026-09-13 15:49 ` 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=20260913145233.1446643-2-caomingpei@gmail.com \
--to=caomingpei@gmail.com \
--cc=ameryhung@gmail.com \
--cc=andrii@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=eddyz87@gmail.com \
--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 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.