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

net/ipv4/ipv6: Replace one-element arraya with flexible-array members

There is a regular need in the kernel to provide a way to declare having
a dynamically sized set of trailing elements in a structure. Kernel code
should always use “flexible array members”[1] for these cases. The older
style of one-element or zero-length arrays should no longer be used[2].

Use an anonymous union with a couple of anonymous structs in order to
keep userspace unchanged and refactor the related code accordingly:

$ pahole -C group_filter net/ipv4/ip_sockglue.o
struct group_filter {
union {
struct {
__u32 gf_interface_aux; /* 0 4 */

/* XXX 4 bytes hole, try to pack */

struct __kernel_sockaddr_storage gf_group_aux; /* 8 128 */
/* --- cacheline 2 boundary (128 bytes) was 8 bytes ago --- */
__u32 gf_fmode_aux; /* 136 4 */
__u32 gf_numsrc_aux; /* 140 4 */
struct __kernel_sockaddr_storage gf_slist[1]; /* 144 128 */
}; /* 0 272 */
struct {
__u32 gf_interface; /* 0 4 */

/* XXX 4 bytes hole, try to pack */

struct __kernel_sockaddr_storage gf_group; /* 8 128 */
/* --- cacheline 2 boundary (128 bytes) was 8 bytes ago --- */
__u32 gf_fmode; /* 136 4 */
__u32 gf_numsrc; /* 140 4 */
struct __kernel_sockaddr_storage gf_slist_flex[0]; /* 144 0 */
}; /* 0 144 */
}; /* 0 272 */

/* size: 272, cachelines: 5, members: 1 */
/* last cacheline: 16 bytes */
};

$ pahole -C compat_group_filter net/ipv4/ip_sockglue.o
struct compat_group_filter {
union {
struct {
__u32 gf_interface_aux; /* 0 4 */
struct __kernel_sockaddr_storage gf_group_aux __attribute__((__aligned__(4))); /* 4 128 */
/* --- cacheline 2 boundary (128 bytes) was 4 bytes ago --- */
__u32 gf_fmode_aux; /* 132 4 */
__u32 gf_numsrc_aux; /* 136 4 */
struct __kernel_sockaddr_storage gf_slist[1] __attribute__((__aligned__(4))); /* 140 128 */
} __attribute__((__packed__)) __attribute__((__aligned__(4))); /* 0 268 */
struct {
__u32 gf_interface; /* 0 4 */
struct __kernel_sockaddr_storage gf_group __attribute__((__aligned__(4))); /* 4 128 */
/* --- cacheline 2 boundary (128 bytes) was 4 bytes ago --- */
__u32 gf_fmode; /* 132 4 */
__u32 gf_numsrc; /* 136 4 */
struct __kernel_sockaddr_storage gf_slist_flex[0] __attribute__((__aligned__(4))); /* 140 0 */
} __attribute__((__packed__)) __attribute__((__aligned__(4))); /* 0 140 */
} __attribute__((__aligned__(1))); /* 0 268 */

/* size: 268, cachelines: 5, members: 1 */
/* forced alignments: 1 */
/* last cacheline: 12 bytes */
} __attribute__((__packed__));

This helps with the ongoing efforts to globally enable -Warray-bounds
and get us closer to being able to tighten the FORTIFY_SOURCE routines
on memcpy().

[1] https://en.wikipedia.org/wiki/Flexible_array_member
[2] https://www.kernel.org/doc/html/v5.10/process/deprecated.html#zero-length-and-one-element-arrays

Link: https://github.com/KSPP/linux/issues/79
Link: https://github.com/KSPP/linux/issues/109
Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Gustavo A. R. Silva and committed by
David S. Miller
db243b79 d15040a3

+55 -30
+20 -7
include/net/compat.h
··· 71 71 } __packed; 72 72 73 73 struct compat_group_filter { 74 - __u32 gf_interface; 75 - struct __kernel_sockaddr_storage gf_group 76 - __aligned(4); 77 - __u32 gf_fmode; 78 - __u32 gf_numsrc; 79 - struct __kernel_sockaddr_storage gf_slist[1] 80 - __aligned(4); 74 + union { 75 + struct { 76 + __u32 gf_interface_aux; 77 + struct __kernel_sockaddr_storage gf_group_aux 78 + __aligned(4); 79 + __u32 gf_fmode_aux; 80 + __u32 gf_numsrc_aux; 81 + struct __kernel_sockaddr_storage gf_slist[1] 82 + __aligned(4); 83 + } __packed; 84 + struct { 85 + __u32 gf_interface; 86 + struct __kernel_sockaddr_storage gf_group 87 + __aligned(4); 88 + __u32 gf_fmode; 89 + __u32 gf_numsrc; 90 + struct __kernel_sockaddr_storage gf_slist_flex[] 91 + __aligned(4); 92 + } __packed; 93 + }; 81 94 } __packed; 82 95 83 96 #endif /* NET_COMPAT_H */
+16 -5
include/uapi/linux/in.h
··· 222 222 }; 223 223 224 224 struct group_filter { 225 - __u32 gf_interface; /* interface index */ 226 - struct __kernel_sockaddr_storage gf_group; /* multicast address */ 227 - __u32 gf_fmode; /* filter mode */ 228 - __u32 gf_numsrc; /* number of sources */ 229 - struct __kernel_sockaddr_storage gf_slist[1]; /* interface index */ 225 + union { 226 + struct { 227 + __u32 gf_interface_aux; /* interface index */ 228 + struct __kernel_sockaddr_storage gf_group_aux; /* multicast address */ 229 + __u32 gf_fmode_aux; /* filter mode */ 230 + __u32 gf_numsrc_aux; /* number of sources */ 231 + struct __kernel_sockaddr_storage gf_slist[1]; /* interface index */ 232 + }; 233 + struct { 234 + __u32 gf_interface; /* interface index */ 235 + struct __kernel_sockaddr_storage gf_group; /* multicast address */ 236 + __u32 gf_fmode; /* filter mode */ 237 + __u32 gf_numsrc; /* number of sources */ 238 + struct __kernel_sockaddr_storage gf_slist_flex[]; /* interface index */ 239 + }; 240 + }; 230 241 }; 231 242 232 243 #define GROUP_FILTER_SIZE(numsrc) \
+10 -9
net/ipv4/ip_sockglue.c
··· 790 790 goto out_free_gsf; 791 791 792 792 err = set_mcast_msfilter(sk, gsf->gf_interface, gsf->gf_numsrc, 793 - gsf->gf_fmode, &gsf->gf_group, gsf->gf_slist); 793 + gsf->gf_fmode, &gsf->gf_group, 794 + gsf->gf_slist_flex); 794 795 out_free_gsf: 795 796 kfree(gsf); 796 797 return err; ··· 800 799 static int compat_ip_set_mcast_msfilter(struct sock *sk, sockptr_t optval, 801 800 int optlen) 802 801 { 803 - const int size0 = offsetof(struct compat_group_filter, gf_slist); 802 + const int size0 = offsetof(struct compat_group_filter, gf_slist_flex); 804 803 struct compat_group_filter *gf32; 805 804 unsigned int n; 806 805 void *p; ··· 814 813 p = kmalloc(optlen + 4, GFP_KERNEL); 815 814 if (!p) 816 815 return -ENOMEM; 817 - gf32 = p + 4; /* we want ->gf_group and ->gf_slist aligned */ 816 + gf32 = p + 4; /* we want ->gf_group and ->gf_slist_flex aligned */ 818 817 819 818 err = -EFAULT; 820 819 if (copy_from_sockptr(gf32, optval, optlen)) ··· 827 826 goto out_free_gsf; 828 827 829 828 err = -EINVAL; 830 - if (offsetof(struct compat_group_filter, gf_slist[n]) > optlen) 829 + if (offsetof(struct compat_group_filter, gf_slist_flex[n]) > optlen) 831 830 goto out_free_gsf; 832 831 833 832 /* numsrc >= (4G-140)/128 overflow in 32 bits */ ··· 835 834 if (n > sock_net(sk)->ipv4.sysctl_igmp_max_msf) 836 835 goto out_free_gsf; 837 836 err = set_mcast_msfilter(sk, gf32->gf_interface, n, gf32->gf_fmode, 838 - &gf32->gf_group, gf32->gf_slist); 837 + &gf32->gf_group, gf32->gf_slist_flex); 839 838 out_free_gsf: 840 839 kfree(p); 841 840 return err; ··· 1456 1455 static int ip_get_mcast_msfilter(struct sock *sk, void __user *optval, 1457 1456 int __user *optlen, int len) 1458 1457 { 1459 - const int size0 = offsetof(struct group_filter, gf_slist); 1458 + const int size0 = offsetof(struct group_filter, gf_slist_flex); 1460 1459 struct group_filter __user *p = optval; 1461 1460 struct group_filter gsf; 1462 1461 int num; ··· 1468 1467 return -EFAULT; 1469 1468 1470 1469 num = gsf.gf_numsrc; 1471 - err = ip_mc_gsfget(sk, &gsf, p->gf_slist); 1470 + err = ip_mc_gsfget(sk, &gsf, p->gf_slist_flex); 1472 1471 if (err) 1473 1472 return err; 1474 1473 if (gsf.gf_numsrc < num) ··· 1482 1481 static int compat_ip_get_mcast_msfilter(struct sock *sk, void __user *optval, 1483 1482 int __user *optlen, int len) 1484 1483 { 1485 - const int size0 = offsetof(struct compat_group_filter, gf_slist); 1484 + const int size0 = offsetof(struct compat_group_filter, gf_slist_flex); 1486 1485 struct compat_group_filter __user *p = optval; 1487 1486 struct compat_group_filter gf32; 1488 1487 struct group_filter gf; ··· 1499 1498 num = gf.gf_numsrc = gf32.gf_numsrc; 1500 1499 gf.gf_group = gf32.gf_group; 1501 1500 1502 - err = ip_mc_gsfget(sk, &gf, p->gf_slist); 1501 + err = ip_mc_gsfget(sk, &gf, p->gf_slist_flex); 1503 1502 if (err) 1504 1503 return err; 1505 1504 if (gf.gf_numsrc < num)
+9 -9
net/ipv6/ipv6_sockglue.c
··· 225 225 if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) 226 226 goto out_free_gsf; 227 227 228 - ret = ip6_mc_msfilter(sk, gsf, gsf->gf_slist); 228 + ret = ip6_mc_msfilter(sk, gsf, gsf->gf_slist_flex); 229 229 out_free_gsf: 230 230 kfree(gsf); 231 231 return ret; ··· 234 234 static int compat_ipv6_set_mcast_msfilter(struct sock *sk, sockptr_t optval, 235 235 int optlen) 236 236 { 237 - const int size0 = offsetof(struct compat_group_filter, gf_slist); 237 + const int size0 = offsetof(struct compat_group_filter, gf_slist_flex); 238 238 struct compat_group_filter *gf32; 239 239 void *p; 240 240 int ret; ··· 249 249 if (!p) 250 250 return -ENOMEM; 251 251 252 - gf32 = p + 4; /* we want ->gf_group and ->gf_slist aligned */ 252 + gf32 = p + 4; /* we want ->gf_group and ->gf_slist_flex aligned */ 253 253 ret = -EFAULT; 254 254 if (copy_from_sockptr(gf32, optval, optlen)) 255 255 goto out_free_p; ··· 261 261 goto out_free_p; 262 262 263 263 ret = -EINVAL; 264 - if (offsetof(struct compat_group_filter, gf_slist[n]) > optlen) 264 + if (offsetof(struct compat_group_filter, gf_slist_flex[n]) > optlen) 265 265 goto out_free_p; 266 266 267 267 ret = ip6_mc_msfilter(sk, &(struct group_filter){ 268 268 .gf_interface = gf32->gf_interface, 269 269 .gf_group = gf32->gf_group, 270 270 .gf_fmode = gf32->gf_fmode, 271 - .gf_numsrc = gf32->gf_numsrc}, gf32->gf_slist); 271 + .gf_numsrc = gf32->gf_numsrc}, gf32->gf_slist_flex); 272 272 273 273 out_free_p: 274 274 kfree(p); ··· 1048 1048 static int ipv6_get_msfilter(struct sock *sk, void __user *optval, 1049 1049 int __user *optlen, int len) 1050 1050 { 1051 - const int size0 = offsetof(struct group_filter, gf_slist); 1051 + const int size0 = offsetof(struct group_filter, gf_slist_flex); 1052 1052 struct group_filter __user *p = optval; 1053 1053 struct group_filter gsf; 1054 1054 int num; ··· 1062 1062 return -EADDRNOTAVAIL; 1063 1063 num = gsf.gf_numsrc; 1064 1064 lock_sock(sk); 1065 - err = ip6_mc_msfget(sk, &gsf, p->gf_slist); 1065 + err = ip6_mc_msfget(sk, &gsf, p->gf_slist_flex); 1066 1066 if (!err) { 1067 1067 if (num > gsf.gf_numsrc) 1068 1068 num = gsf.gf_numsrc; ··· 1077 1077 static int compat_ipv6_get_msfilter(struct sock *sk, void __user *optval, 1078 1078 int __user *optlen) 1079 1079 { 1080 - const int size0 = offsetof(struct compat_group_filter, gf_slist); 1080 + const int size0 = offsetof(struct compat_group_filter, gf_slist_flex); 1081 1081 struct compat_group_filter __user *p = optval; 1082 1082 struct compat_group_filter gf32; 1083 1083 struct group_filter gf; ··· 1100 1100 return -EADDRNOTAVAIL; 1101 1101 1102 1102 lock_sock(sk); 1103 - err = ip6_mc_msfget(sk, &gf, p->gf_slist); 1103 + err = ip6_mc_msfget(sk, &gf, p->gf_slist_flex); 1104 1104 release_sock(sk); 1105 1105 if (err) 1106 1106 return err;