bpf: Check tail zero of bpf_map_info and bpf_prog_info
Subsystems (4)
v3 cover bpf: Check tail zero of bpf_map_info and bpf_prog_info
Check the tail bytes of bpf_map_info and bpf_prog_info due to padding when getting map info and prog info via BPF_OBJ_GET_INFO_BY_FD, which was discussed in the thread
"bpf: Check tail zero of bpf_common_attr using offsetofend" [1].
Links:
[1] https://lore.kernel.org/bpf/[email protected]/
Changes:
v2 -> v3:
* Add "__u32 :32" to bpf_map_info and bpf_prog_info (per Alexei).
* v2: https://lore.kernel.org/bpf/[email protected]/
v1 -> v2:
* Collect Acked-by tags from Mykyta, thanks.
* Update Fixes tag in patch #2 (per bot+bpf-ci)
* v1: https://lore.kernel.org/bpf/[email protected]/
Leon Hwang (3):
bpf: Check tail zero of bpf_map_info
bpf: Check tail zero of bpf_prog_info selftests/bpf: Add tests to verify checking padding bytes for
bpf_[map,prog]_info
include/uapi/linux/bpf.h | 2 + kernel/bpf/syscall.c | 10 ++-- tools/include/uapi/linux/bpf.h | 2 + .../selftests/bpf/prog_tests/bpf_attr_size.c | 55 +++++++++++++++++++ 4 files changed, 65 insertions(+), 4 deletions(-)
Signature · 1 line
2.54.0
PATCH v3 1/3 bpf: Check tail zero of bpf_map_info
Since there're 4 bytes padding at the end of struct bpf_map_info, they won't be checked by bpf_check_uarg_tail_zero().
pahole -C bpf_map_info ./vmlinux
struct bpf_map_info {
...
__u64 hash __attribute__((__aligned__(8))); /* 88 8 */
__u32 hash_size; /* 96 4 */
/* size: 104, cachelines: 2, members: 18 */
/* padding: 4 */
/* forced alignments: 1 */
/* last cacheline: 40 bytes */
} __attribute__((__aligned__(8)));If a future kernel extension adds a new 4-byte field, older userspace programs allocating this structure on the stack might inadvertently pass uninitialized stack garbage into the new field, permanently breaking backward compatibility. -- sashiko [1]
Fix it by changing sizeof(info) tooffsetofend(struct bpf_map_info, hash_size).
And, add "__u32 :32" to the tail of struct bpf_map_info.
[1] https://lore.kernel.org/bpf/[email protected]/
--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 5 +++-- tools/include/uapi/linux/bpf.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +1 -0
@@ -6733,6 +6733,7 @@ struct bpf_map_info {__u64 map_extra;__aligned_u64 hash;__u32 hash_size; + __u32 :32 ; } __attribute__((aligned(8)));struct bpf_btf_info {
kernel/bpf/syscall.c +3 -2
@@ -5406,10 +5406,11 @@ static int bpf_map_get_info_by_fd(struct file *file, {struct bpf_map_info __user *uinfo =u64_to_user_ptr (attr->info.info);struct bpf_map_info info; -u32 info_len = attr->info.info_len; +u32 info_len = attr->info.info_len, len;int err; - err =bpf_check_uarg_tail_zero (USER_BPFPTR (uinfo),sizeof (info), info_len); + len = offsetofend(struct bpf_map_info, hash_size); + err =bpf_check_uarg_tail_zero (USER_BPFPTR (uinfo), len, info_len);if (err)return err; info_len =min_t (u32,sizeof (info), info_len);
tools/include/uapi/linux/bpf.h +1 -0
@@ -6733,6 +6733,7 @@ struct bpf_map_info {__u64 map_extra;__aligned_u64 hash;__u32 hash_size; + __u32 :32 ; } __attribute__((aligned(8)));struct bpf_btf_info {
Signature · 1 line
2.54.0
PATCH v3 2/3 bpf: Check tail zero of bpf_prog_info
In reply to Leon HwangSince there're 4 bytes padding at the end of struct bpf_prog_info, they won't be checked by bpf_check_uarg_tail_zero().
pahole -C bpf_prog_info ./vmlinux
struct bpf_prog_info {
...
__u32 attach_btf_obj_id; /* 220 4 */
__u32 attach_btf_id; /* 224 4 */
/* size: 232, cachelines: 4, members: 38 */
/* sum members: 224 */
/* sum bitfield members: 1 bits, bit holes: 1, sum bit holes: 31 bits */
/* padding: 4 */
/* forced alignments: 9 */
/* last cacheline: 40 bytes */
} __attribute__((__aligned__(8)));If a future kernel extension adds a new 4-byte field, older userspace programs allocating this structure on the stack might inadvertently pass uninitialized stack garbage into the new field, permanently breaking backward compatibility. -- sashiko [1]
Fix it by changing sizeof(info) tooffsetofend(struct bpf_prog_info, attach_btf_id).
And, add "__u32 :32" to the tail of struct bpf_prog_info.
[1] https://lore.kernel.org/bpf/[email protected]/
--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 5 +++-- tools/include/uapi/linux/bpf.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +1 -0
@@ -6712,6 +6712,7 @@ struct bpf_prog_info {__u32 verified_insns;__u32 attach_btf_obj_id;__u32 attach_btf_id; + __u32 :32 ; } __attribute__((aligned(8)));struct bpf_map_info {
kernel/bpf/syscall.c +3 -2
@@ -5121,10 +5121,11 @@ static int bpf_prog_get_info_by_fd(struct file *file,u32 info_len = attr->info.info_len;struct bpf_prog_kstats stats;char __user *uinsns; -u32 ulen; +u32 ulen, len;int err; - err =bpf_check_uarg_tail_zero (USER_BPFPTR (uinfo),sizeof (info), info_len); + len = offsetofend(struct bpf_prog_info, attach_btf_id); + err =bpf_check_uarg_tail_zero (USER_BPFPTR (uinfo), len, info_len);if (err)return err; info_len =min_t (u32,sizeof (info), info_len);
tools/include/uapi/linux/bpf.h +1 -0
@@ -6712,6 +6712,7 @@ struct bpf_prog_info {__u32 verified_insns;__u32 attach_btf_obj_id;__u32 attach_btf_id; + __u32 :32 ; } __attribute__((aligned(8)));struct bpf_map_info {
Signature · 1 line
2.54.0
[email protected]
AI reviewRe: [PATCH bpf-next v3 2/3] bpf: Check tail zero of bpf_prog_info
Is the Fixes: tag pointing to the right commit? The padding bug was actually introduced by commit b79c9fc9551b ("bpf: implement BPF_PROG_QUERY for BPF_LSM_CGROUP"), which added attach_btf_obj_id and…
Quoted 28 lines
commit
acd0d2042870d7613c0c27932c41262d1c41b4e1
Author: Leon Hwang <[email protected]>
Commit: Kernel Patches Daemon <[email protected]>bpf: Check tail zero of
bpf_prog_infoSince there're 4 bytes padding at the end of struct
bpf_prog_info, they won't be checked bybpf_check_uarg_tail_zero().pahole -C
bpf_prog_info./vmlinux
structbpf_prog_info{
...
__u32attach_btf_obj_id; /* 220 4 */
__u32attach_btf_id; /* 224 4 *//* size: 232, cachelines: 4, members: 38 */
/* sum members: 224 */
/* sum bitfield members: 1 bits, bit holes: 1, sum bit holes: 31 bits */ /* padding: 4 */
/* forced alignments: 9 */
/* last cacheline: 40 bytes */
} __attribute__((__aligned__(8)));If a future kernel extension adds a new 4-byte field, older userspace programs allocating this structure on the stack might inadvertently pass uninitialized stack garbage into the new field, permanently breaking backward compatibility. -- sashiko [1]
Quoted 8 lines
Fix it by changing sizeof(info) to
offsetofend(struct bpf_prog_info, attach_btf_id).And, add "
__u32:32" to the tail of structbpf_prog_info.[1] https://lore.kernel.org/bpf/[email protected]/
Fixes aba64c7da983 ("bpf: Add verified_insns to bpf_prog_info and fdinfo")
Is the Fixes: tag pointing to the right commit? The padding bug was actually introduced by commit b79c9fc9551b ("bpf: implement BPF_PROG_QUERY for BPF_LSM_CGROUP"), which added attach_btf_obj_id and attach_btf_id after verified_insns, creating the 4 bytes of padding at the end of the structure. Commit aba64c7da983 added verified_insns as the last field but did not create padding.
Suggested: Fixes: b79c9fc9551b ("bpf: implement BPF_PROG_QUERY for BPF_LSM_CGROUP")
Quoted 2 lines
Acked-by Mykyta Yatsenko <[email protected]>Signed-off-by Leon Hwang <[email protected]>
[ ... ]
Quoted 8 lines
kernel/bpf/syscall.c +0
-0@@ -5121,10 +5121,11 @@ static int bpf_prog_get_info_by_fd(struct file *file,u32 info_len = attr->info.info_len;struct bpf_prog_kstats stats;char __user *uinsns;
kernel/bpf/syscall.c static int bpf_prog_get_info_by_fd(struct file *file, · line 5131
Quoted 10 lines
kernel/bpf/syscall.c +3
-2-u32 ulen; +u32 ulen, len;int err; - err =bpf_check_uarg_tail_zero (USER_BPFPTR (uinfo),sizeof (info), info_len); + len = offsetofend(struct bpf_prog_info, attach_btf_id); + err =bpf_check_uarg_tail_zero (USER_BPFPTR (uinfo), len, info_len);if (err)return err; info_len =min_t (u32,sizeof (info), info_len);
Does this change break the ABI for existing deployed binaries?
Previously, expected_size was 232 (sizeof(info)). If an existing binary passes info_len = 232 with uninitialized stack garbage in the 4-byte padding, bpf_check_uarg_tail_zero() would see actual_size <= expected_size (232 <= 232) and immediately return 0.
By reducing expected_size to 228, actual_size > expected_size (232 > 228), so bpf_check_uarg_tail_zero() will actively check the 4 padding bytes and return -E2BIG if they contain garbage. This rejects previously working applications.
sashiko-bot and bot+bpf-ci raised this concern in v1 and v2:
https://lore.kernel.org/bpf/[email protected]/
While v3 adds '__u32 :32;' to prevent future field additions in the padding space (addressing forward compatibility), the backward compatibility concern about breaking existing binaries remains unresolved.
[ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/27025811732
PATCH v3 3/3 selftests/bpf: Add tests to verify checking padding bytes for bpf_[map,prog]_info
In reply to Leon HwangAdd two tests to verify that the tail padding 4 bytes of struct bpf_map_info and bpf_prog_info are checked in syscall.c using bpf_check_uarg_tail_zero().
--- .../selftests/bpf/prog_tests/bpf_attr_size.c | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+)
tools/testing/selftests/bpf/prog_tests/bpf_attr_size.c +55 -0
@@ -62,8 +62,63 @@ static void test_query_size_boundaries(void)cgroup_skb_direct_packet_access__destroy (skel); } +static void test_map_info_tail_zero (void ) +{ +LIBBPF_OPTS (bpf_map_create_opts, map_opts); +struct bpf_map_info_fake { + __u8 info[offsetofend(struct bpf_map_info, hash_size)]; +__u32 pad; + } info = { + .pad =1 , + }; +int map_fd, err; +__u32 info_len; + + map_fd =bpf_map_create (BPF_MAP_TYPE_ARRAY ,"arr" ,sizeof (int ),1 ,1 , &map_opts); +if (!ASSERT_GE (map_fd,0 ,"bpf_map_create" )) +return ; + + info_len =sizeof (info); + err =bpf_obj_get_info_by_fd (map_fd, &info, &info_len); +ASSERT_EQ (err, -E2BIG ,"bpf_obj_get_info_by_fd" ); + +close (map_fd); +} + +static void test_prog_info_tail_zero (void ) +{ +LIBBPF_OPTS (bpf_prog_load_opts, prog_opts); +struct bpf_insn insns[] = { +BPF_MOV64_IMM (BPF_REG_0 ,0 ), +BPF_EXIT_INSN (), + }; +struct bpf_prog_info_fake { + __u8 info[offsetofend(struct bpf_prog_info, attach_btf_id)]; +__u32 pad; + } info = { + .pad =1 , + }; +int prog_fd, err; +__u32 info_len; + + prog_fd =bpf_prog_load (BPF_PROG_TYPE_SOCKET_FILTER ,"test_prog" ,"GPL" , insns, +ARRAY_SIZE (insns), &prog_opts); +if (!ASSERT_GE (prog_fd,0 ,"bpf_prog_load" )) +return ; + + info_len =sizeof (info); + err =bpf_obj_get_info_by_fd (prog_fd, &info, &info_len); +ASSERT_EQ (err, -E2BIG ,"bpf_obj_get_info_by_fd" ); + +close (prog_fd); +} +void test_bpf_attr_size (void ) {if (test__start_subtest ("query_size_boundaries" ))test_query_size_boundaries (); +if (test__start_subtest ("map_info_tail_zero" )) +test_map_info_tail_zero (); +if (test__start_subtest ("prog_info_tail_zero" )) +test_prog_info_tail_zero (); }
Signature · 1 line
2.54.0
This series was applied to bpf/bpf-next.git (master) by Alexei Starovoitov <[email protected]>:
Hello:
This series was applied to bpf/bpf-next.git (master) by Alexei Starovoitov <[email protected]>:
Quoted 9 lines · On Fri, 5 Jun 2026 23:52:46 +0800 you wrote:
Check the tail bytes of
bpf_map_infoandbpf_prog_infodue to padding when getting map info and prog info viaBPF_OBJ_GET_INFO_BY_FD, which was discussed in the thread
"bpf: Check tail zero ofbpf_common_attrusing offsetofend" [1].Links:
[1] https://lore.kernel.org/bpf/[email protected]/[...]
Here is the summary with links:
- [bpf-next,v3,1/3] bpf: Check tail zero of bpf_map_info
https://git.kernel.org/bpf/bpf-next/c/e2a49fdb1bee - [bpf-next,v3,2/3] bpf: Check tail zero of bpf_prog_info https://git.kernel.org/bpf/bpf-next/c/786be2b05980 - [bpf-next,v3,3/3] selftests/bpf: Add tests to verify checking padding bytes for bpf_[map,prog]_info https://git.kernel.org/bpf/bpf-next/c/d47e67a487bf
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html