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

Configure Feed

Select the types of activity you want to include in your feed.

sched/topology: Fix sched_domain_span()

Commit 8e8e23dea43e ("sched/topology: Compute sd_weight considering
cpuset partitions") ends up relying on the fact that structure
initialization should not touch the flexible array.

However, the official GCC specification for "Arrays of Length Zero"
[*] says:

Although the size of a zero-length array is zero, an array member of
this kind may increase the size of the enclosing type as a result of
tail padding.

Additionally, structure initialization will zero tail padding. With
the end result that since offsetof(*type, member) < sizeof(*type),
array initialization will clobber the flex array.

Luckily, the way flexible array sizes are calculated is:

sizeof(*type) + count * sizeof(*type->member)

This means we have the complete size of the flex array *outside* of
sizeof(*type), so use that instead of relying on the broken flex array
definition.

[*] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html

Fixes: 8e8e23dea43e ("sched/topology: Compute sd_weight considering cpuset partitions")
Reported-by: Nathan Chancellor <nathan@kernel.org>
Debugged-by: K Prateek Nayak <kprateek.nayak@amd.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: Jon Hunter <jonathanh@nvidia.com>
Tested-by: Chen Yu <yu.c.chen@intel.com>
Tested-by: K Prateek Nayak <kprateek.nayak@amd.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Link: https://patch.msgid.link/20260323093627.GY3738010@noisy.programming.kicks-ass.net

+18 -6
+18 -6
include/linux/sched/topology.h
··· 142 142 143 143 unsigned int span_weight; 144 144 /* 145 - * Span of all CPUs in this domain. 145 + * See sched_domain_span(), on why flex arrays are broken. 146 146 * 147 - * NOTE: this field is variable length. (Allocated dynamically 148 - * by attaching extra space to the end of the structure, 149 - * depending on how many CPUs the kernel has booted up with) 150 - */ 151 147 unsigned long span[]; 148 + */ 152 149 }; 153 150 154 151 static inline struct cpumask *sched_domain_span(struct sched_domain *sd) 155 152 { 156 - return to_cpumask(sd->span); 153 + /* 154 + * Turns out that C flexible arrays are fundamentally broken since it 155 + * is allowed for offsetof(*sd, span) < sizeof(*sd), this means that 156 + * structure initialzation *sd = { ... }; which writes every byte 157 + * inside sizeof(*type), will over-write the start of the flexible 158 + * array. 159 + * 160 + * Luckily, the way we allocate sched_domain is by: 161 + * 162 + * sizeof(*sd) + cpumask_size() 163 + * 164 + * this means that we have sufficient space for the whole flex array 165 + * *outside* of sizeof(*sd). So use that, and avoid using sd->span. 166 + */ 167 + unsigned long *bitmap = (void *)sd + sizeof(*sd); 168 + return to_cpumask(bitmap); 157 169 } 158 170 159 171 extern void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[],