Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: Convert struct sockaddr to fixed-size "sa_data[14]"

Revert struct sockaddr from flexible array to fixed 14-byte "sa_data",
to solve over 36,000 -Wflex-array-member-not-at-end warnings, since
struct sockaddr is embedded within many network structs.

With socket/proto sockaddr-based internal APIs switched to use struct
sockaddr_unsized, there should be no more uses of struct sockaddr that
depend on reading beyond the end of struct sockaddr::sa_data that might
trigger bounds checking.

Comparing an x86_64 "allyesconfig" vmlinux build before and after this
patch showed no new "ud1" instructions from CONFIG_UBSAN_BOUNDS nor any
new "field-spanning" memcpy CONFIG_FORTIFY_SOURCE instrumentations.

Cc: Gustavo A. R. Silva <gustavo@embeddedor.com>
Signed-off-by: Kees Cook <kees@kernel.org>
Link: https://patch.msgid.link/20251104002617.2752303-8-kees@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Kees Cook and committed by
Jakub Kicinski
2b5e9f9b c1a799ee

+11 -16
+2 -4
include/linux/socket.h
··· 32 32 * 1003.1g requires sa_family_t and that sa_data is char. 33 33 */ 34 34 35 + /* Deprecated for in-kernel use. Use struct sockaddr_unsized instead. */ 35 36 struct sockaddr { 36 37 sa_family_t sa_family; /* address family, AF_xxx */ 37 - union { 38 - char sa_data_min[14]; /* Minimum 14 bytes of protocol address */ 39 - DECLARE_FLEX_ARRAY(char, sa_data); 40 - }; 38 + char sa_data[14]; /* 14 bytes of protocol address */ 41 39 }; 42 40 43 41 /**
+1 -1
net/core/dev.c
··· 9973 9973 /* "sa" is a true struct sockaddr with limited "sa_data" member. */ 9974 9974 int netif_get_mac_address(struct sockaddr *sa, struct net *net, char *dev_name) 9975 9975 { 9976 - size_t size = sizeof(sa->sa_data_min); 9976 + size_t size = sizeof(sa->sa_data); 9977 9977 struct net_device *dev; 9978 9978 int ret = 0; 9979 9979
+1 -1
net/core/dev_ioctl.c
··· 596 596 if (ifr->ifr_hwaddr.sa_family != dev->type) 597 597 return -EINVAL; 598 598 memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, 599 - min(sizeof(ifr->ifr_hwaddr.sa_data_min), 599 + min(sizeof(ifr->ifr_hwaddr.sa_data), 600 600 (size_t)dev->addr_len)); 601 601 netdev_lock_ops(dev); 602 602 call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+1 -1
net/ipv4/arp.c
··· 1189 1189 1190 1190 read_lock_bh(&neigh->lock); 1191 1191 memcpy(r->arp_ha.sa_data, neigh->ha, 1192 - min(dev->addr_len, sizeof(r->arp_ha.sa_data_min))); 1192 + min(dev->addr_len, sizeof(r->arp_ha.sa_data))); 1193 1193 r->arp_flags = arp_state_to_flags(neigh); 1194 1194 read_unlock_bh(&neigh->lock); 1195 1195
+5 -5
net/packet/af_packet.c
··· 3284 3284 { 3285 3285 struct sock *sk = sock->sk; 3286 3286 struct sockaddr *sa = (struct sockaddr *)uaddr; 3287 - char name[sizeof(sa->sa_data_min) + 1]; 3287 + char name[sizeof(sa->sa_data) + 1]; 3288 3288 3289 3289 /* 3290 3290 * Check legality ··· 3295 3295 /* uaddr->sa_data comes from the userspace, it's not guaranteed to be 3296 3296 * zero-terminated. 3297 3297 */ 3298 - memcpy(name, sa->sa_data, sizeof(sa->sa_data_min)); 3299 - name[sizeof(sa->sa_data_min)] = 0; 3298 + memcpy(name, sa->sa_data, sizeof(sa->sa_data)); 3299 + name[sizeof(sa->sa_data)] = 0; 3300 3300 3301 3301 return packet_do_bind(sk, name, 0, 0); 3302 3302 } ··· 3581 3581 return -EOPNOTSUPP; 3582 3582 3583 3583 uaddr->sa_family = AF_PACKET; 3584 - memset(uaddr->sa_data, 0, sizeof(uaddr->sa_data_min)); 3584 + memset(uaddr->sa_data, 0, sizeof(uaddr->sa_data)); 3585 3585 rcu_read_lock(); 3586 3586 dev = dev_get_by_index_rcu(sock_net(sk), READ_ONCE(pkt_sk(sk)->ifindex)); 3587 3587 if (dev) 3588 - strscpy(uaddr->sa_data, dev->name, sizeof(uaddr->sa_data_min)); 3588 + strscpy(uaddr->sa_data, dev->name, sizeof(uaddr->sa_data)); 3589 3589 rcu_read_unlock(); 3590 3590 3591 3591 return sizeof(*uaddr);
+1 -4
tools/perf/trace/beauty/include/linux/socket.h
··· 34 34 35 35 struct sockaddr { 36 36 sa_family_t sa_family; /* address family, AF_xxx */ 37 - union { 38 - char sa_data_min[14]; /* Minimum 14 bytes of protocol address */ 39 - DECLARE_FLEX_ARRAY(char, sa_data); 40 - }; 37 + char sa_data[14]; /* 14 bytes of protocol address */ 41 38 }; 42 39 43 40 struct linger {