Re: [PATCH] bpf: avoid padding in struct bpf_common_attr

From: Arnd Bergmann

Date: Wed May 20 2026 - 02:39:58 EST


On Wed, May 20, 2026, at 00:17, Yonghong Song wrote:
> On 5/19/26 1:38 PM, Arnd Bergmann wrote:
>> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
>> index cc4c0b62cdfa..0f0ec00cae4a 100644
>> --- a/include/uapi/linux/bpf.h
>> +++ b/include/uapi/linux/bpf.h
>> @@ -1507,6 +1507,7 @@ struct bpf_common_attr {
>> __u32 log_size;
>> __u32 log_level;
>> __u32 log_true_size;
>> + __u32 __pad;
>
> Let us make it as
> __u32 :32;
> ?
> This can avoid people to use the '__pad' name.

Sure. Do you want me to resend, are are you going to change
it yourself?

> IIUC, typically uapi struct won't have implicit padding.

Correct. I have a patch set that I'm testing to add the
Warning to the kernel build process to find these
automatically, but I first need to take care of all the
existing instances and only report newly added ones at
the moment.

FWIW, see below for what I currently have in my private
tree for linux/bpf.h. Most of these ones are architecture
independent, so I just do the same '__u32 :32' that you
suggested above. For bpf_cgroup_storage_key and
bpf_attr::link_create::{tracing,tcx,expected_revision,cgroup}
there is an additional problem sinc ethe 64-bit fields
use __u64 instead of __aligned_u64.

This makes the structure layout incompatible between
i386 and x86_64. In the version below I use a
__uapi_arch_pad32 macro to have architecture specific
padding that keeps the existing behavior, but this
technically also requires adding a compat syscall
on x86 (but no other compat architectures) to convert
between the two layouts.
Using __aligned_u64 consistently here would have avoided
this problem, but changing it later is not an ABI
change that would affect every architecture with
unaligned __u64 (arc, csky, m68k, microblaze, nios2,
openrisc, sh and i386).

Arnd


diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index aec171ccb6ef..0f0ec00cae4a 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -111,7 +111,8 @@ struct bpf_lpm_trie_key_u8 {
struct bpf_cgroup_storage_key {
__u64 cgroup_inode_id; /* cgroup inode id */
__u32 attach_type; /* program attach type (enum bpf_attach_type) */
-};
+ __uapi_arch_pad32;
+} __uapi_arch_align;

enum bpf_cgroup_iter_order {
BPF_CGROUP_ITER_ORDER_UNSPEC = 0,
@@ -1562,10 +1564,12 @@ union bpf_attr {
__aligned_u64 excl_prog_hash;
/* Size of the passed excl_prog_hash. */
__u32 excl_prog_hash_size;
+ __u32 :32;
};

struct { /* anonymous struct used by BPF_MAP_*_ELEM and BPF_MAP_FREEZE commands */
__u32 map_fd;
+ __u32 :32;
__aligned_u64 key;
union {
__aligned_u64 value;
@@ -1668,6 +1672,7 @@ union bpf_attr {
* if BPF_F_PATH_FD flag is not set, AT_FDCWD is assumed.
*/
__s32 path_fd;
+ __u32 :32;
};

struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
@@ -1708,6 +1713,7 @@ union bpf_attr {
__u32 flags;
__u32 cpu;
__u32 batch_size;
+ __u32 :32;
} test;

struct { /* anonymous struct used by BPF_*_GET_*_ID */
@@ -1809,6 +1815,7 @@ union bpf_attr {
struct {
__aligned_u64 iter_info; /* extra bpf_iter_link_info */
__u32 iter_info_len; /* iter_info length */
+ __u32 :32;
};
struct {
/* black box user-provided value passed through
@@ -1827,12 +1834,13 @@ union bpf_attr {
struct {
/* this is overlaid with the target_btf_id above. */
__u32 target_btf_id;
+ __uapi_arch_pad32;
/* black box user-provided value passed through
* to BPF program at the execution time and
* accessible through bpf_get_attach_cookie() BPF helper
*/
__u64 cookie;
- } tracing;
+ } __uapi_arch_align tracing;
struct {
__u32 pf;
__u32 hooknum;
@@ -1844,8 +1852,9 @@ union bpf_attr {
__u32 relative_fd;
__u32 relative_id;
};
+ __uapi_arch_pad32;
__u64 expected_revision;
- } tcx;
+ } __uapi_arch_align tcx;
struct {
__aligned_u64 path;
__aligned_u64 offsets;
@@ -1854,12 +1863,14 @@ union bpf_attr {
__u32 cnt;
__u32 flags;
__u32 pid;
+ __u32 :32;
} uprobe_multi;
struct {
union {
__u32 relative_fd;
__u32 relative_id;
};
+ __uapi_arch_pad32;
__u64 expected_revision;
} netkit;
struct {
@@ -1867,6 +1878,7 @@ union bpf_attr {
__u32 relative_fd;
__u32 relative_id;
};
+ __uapi_arch_pad32;
__u64 expected_revision;
} cgroup;
};
@@ -1922,6 +1934,7 @@ union bpf_attr {
__u32 stream_buf_len;
__u32 stream_id;
__u32 prog_fd;
+ __u32 :32;
} prog_stream_read;

struct {
@@ -6648,6 +6661,7 @@ struct sk_reuseport_md {
__u32 ip_protocol; /* IP protocol. e.g. IPPROTO_TCP, IPPROTO_UDP */
__u32 bind_inany; /* Is sock bound to an INANY address? */
__u32 hash; /* A hash of the packet 4 tuples */
+ __u32 :32;
/* When reuse->migrating_sk is NULL, it is selecting a sk for the
* new incoming connection request (e.g. selecting a listen sk for
* the received SYN in the TCP case). reuse->sk is one of the sk
@@ -6706,6 +6720,7 @@ struct bpf_prog_info {
__u32 verified_insns;
__u32 attach_btf_obj_id;
__u32 attach_btf_id;
+ __u32 :32; /* alignment pad */
} __attribute__((aligned(8)));

struct bpf_map_info {
@@ -6727,6 +6742,7 @@ struct bpf_map_info {
__u64 map_extra;
__aligned_u64 hash;
__u32 hash_size;
+ __u32 :32; /* alignment pad */
} __attribute__((aligned(8)));

struct bpf_btf_info {
@@ -6742,6 +6758,7 @@ struct bpf_link_info {
__u32 type;
__u32 id;
__u32 prog_id;
+ __u32 :32; /* pad to alignof(__aligned_u64) */
union {
struct {
__aligned_u64 tp_name; /* in/out: tp_name buffer ptr */
@@ -6759,6 +6776,7 @@ struct bpf_link_info {
struct {
__u64 cgroup_id;
__u32 attach_type;
+ __uapi_arch_pad32;
} cgroup;
struct {
__aligned_u64 target_name; /* in/out: target_name buffer ptr */
@@ -6777,11 +6795,16 @@ struct bpf_link_info {
struct {
__u64 cgroup_id;
__u32 order;
+ __uapi_arch_pad32;
} cgroup;
struct {
__u32 tid;
__u32 pid;
} task;
+ struct {
+ __u64 :64;
+ __u64 :64;
+ };
};
} iter;
struct {
@@ -6896,6 +6919,7 @@ struct bpf_sock_addr {
__u32 msg_src_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write.
* Stored in network byte order.
*/
+ __u32 :32; /* pad to alignof(u64) */
__bpf_md_ptr(struct bpf_sock *, sk);
};

@@ -7555,6 +7579,7 @@ struct bpf_sk_lookup {
__u32 local_ip6[4]; /* Network byte order */
__u32 local_port; /* Host byte order */
__u32 ingress_ifindex; /* The arriving interface. Determined by inet_iif. */
+ __u32 :32;
};

/*