* [PATCH 1/4] ARM: cacheinfo: avoid out-of-bounds write in populate_cache_leaves()
2026-09-12 19:55 [PATCH 0/4] ARM: allocate the cacheinfo early to fix the PREEMPT_RT boot warning Karl Mehltretter
@ 2026-09-12 19:55 ` Karl Mehltretter
2026-09-12 19:55 ` [PATCH 2/4] ARM: cacheinfo: count external caches in early_cache_level() Karl Mehltretter
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Karl Mehltretter @ 2026-09-12 19:55 UTC (permalink / raw)
To: Russell King, Dmitry Baryshkov, Sudeep Holla
Cc: Karl Mehltretter, Pierre Gondois, Linus Walleij, Radu Rendec,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
linux-arm-kernel, linux-rt-devel, linux-kernel, stable
populate_cache_leaves() advances its bounds-checking index once per
cache level, but split instruction/data caches consume two entries.
CLIDR-based allocation supplies enough entries. Early allocation from
the device tree can supply fewer and expose an out-of-bounds write.
Count each written leaf and stop before a split level that does not
fit. This prepares ARM for DT-based early allocation and matches
commit 875d742cf532 ("arm64: cacheinfo: Avoid out-of-bounds write to
cacheinfo array").
Fixes: a9ff94477836 ("ARM: 9433/2: implement cacheinfo support")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
The overrun was reproduced with KASAN on QEMU virt, cortex-a15, using
a device tree whose cpu nodes carry only d-cache-size, so one leaf.
With patch 4 alone the boot reports a slab-out-of-bounds write in
populate_cache_leaves(). With this patch the boot is clean.
arch/arm/kernel/cacheinfo.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
index e1469b641780..860eeb03cfe5 100644
--- a/arch/arm/kernel/cacheinfo.c
+++ b/arch/arm/kernel/cacheinfo.c
@@ -151,7 +151,7 @@ int populate_cache_leaves(unsigned int cpu)
unsigned int level, idx;
enum cache_type type;
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
- struct cacheinfo *this_leaf = this_cpu_ci->info_list;
+ struct cacheinfo *infos = this_cpu_ci->info_list;
unsigned int arch = cpu_architecture();
/* CLIDR is not present before ARMv7/v7m */
@@ -159,13 +159,15 @@ int populate_cache_leaves(unsigned int cpu)
return -EOPNOTSUPP;
for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
- idx < this_cpu_ci->num_leaves; idx++, level++) {
+ idx < this_cpu_ci->num_leaves; level++) {
type = get_cache_type(level);
if (type == CACHE_TYPE_SEPARATE) {
- ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
- ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level);
+ if (idx + 1 >= this_cpu_ci->num_leaves)
+ break;
+ ci_leaf_init(&infos[idx++], CACHE_TYPE_DATA, level);
+ ci_leaf_init(&infos[idx++], CACHE_TYPE_INST, level);
} else {
- ci_leaf_init(this_leaf++, type, level);
+ ci_leaf_init(&infos[idx++], type, level);
}
}
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 2/4] ARM: cacheinfo: count external caches in early_cache_level()
2026-09-12 19:55 [PATCH 0/4] ARM: allocate the cacheinfo early to fix the PREEMPT_RT boot warning Karl Mehltretter
2026-09-12 19:55 ` [PATCH 1/4] ARM: cacheinfo: avoid out-of-bounds write in populate_cache_leaves() Karl Mehltretter
@ 2026-09-12 19:55 ` Karl Mehltretter
2026-09-12 20:09 ` sashiko-bot
2026-09-12 19:55 ` [PATCH 3/4] ARM: cacheinfo: guard the CLIDR read in populate_cache_leaves() Karl Mehltretter
2026-09-12 19:55 ` [PATCH 4/4] ARM: topology: allocate the cacheinfo early on the boot CPU Karl Mehltretter
3 siblings, 1 reply; 6+ messages in thread
From: Karl Mehltretter @ 2026-09-12 19:55 UTC (permalink / raw)
To: Russell King, Dmitry Baryshkov, Sudeep Holla
Cc: Karl Mehltretter, Pierre Gondois, Linus Walleij, Radu Rendec,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
linux-arm-kernel, linux-rt-devel, linux-kernel, stable
early_cache_level() counts only caches described by CLIDR. An external
PL310 is absent from CLIDR, and its arm,pl310-cache compatible is not
accepted by init_of_cache_level(), so early allocation falls back to
an incomplete count. init_cache_level() later adds the DT-described
L2, forcing reallocation on the secondary CPU with interrupts disabled.
This defeats early allocation on PREEMPT_RT.
Use init_cache_level() for early sizing too, so the initial allocation
includes external caches described by the device tree.
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
Without this patch, patch 4 keeps the warning on QEMU vexpress-a9
(4 Cortex-A9, PL310): the early size is 2 leaves, init_cache_level()
wants 3, and init_level_allocate_ci() reallocates on the secondary
CPU. With it the warning is gone and the cache sysfs tree is unchanged
(L1 data, L1 instruction, L2 unified shared by all CPUs).
arch/arm/kernel/cacheinfo.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
index 860eeb03cfe5..31591c947254 100644
--- a/arch/arm/kernel/cacheinfo.c
+++ b/arch/arm/kernel/cacheinfo.c
@@ -111,13 +111,6 @@ static int detect_cache_level(unsigned int *level_p, unsigned int *leaves_p)
return 0;
}
-int early_cache_level(unsigned int cpu)
-{
- struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
-
- return detect_cache_level(&this_cpu_ci->num_levels, &this_cpu_ci->num_leaves);
-}
-
int init_cache_level(unsigned int cpu)
{
unsigned int level, leaves;
@@ -146,6 +139,11 @@ int init_cache_level(unsigned int cpu)
return 0;
}
+int early_cache_level(unsigned int cpu)
+{
+ return init_cache_level(cpu);
+}
+
int populate_cache_leaves(unsigned int cpu)
{
unsigned int level, idx;
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH 2/4] ARM: cacheinfo: count external caches in early_cache_level()
2026-09-12 19:55 ` [PATCH 2/4] ARM: cacheinfo: count external caches in early_cache_level() Karl Mehltretter
@ 2026-09-12 20:09 ` sashiko-bot
0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-09-12 20:09 UTC (permalink / raw)
To: Karl Mehltretter; +Cc: linux-rt-devel, linux-kernel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Reallocation of cacheinfo memory via kzalloc_objs(..., GFP_ATOMIC) is called with interrupts disabled on secondary CPUs in big.LITTLE systems, which crashes on PREEMPT_RT.
--
commit 19c5ea8e9a4a8fc2bc601139e06e4ead6e749ab2
Author: Karl Mehltretter <kmehltretter@gmail.com>
ARM: cacheinfo: count external caches in early_cache_level()
This commit alters early_cache_level() to directly invoke
init_cache_level(). This includes device tree-described external
caches in the early allocation size, attempting to avoid late cacheinfo
reallocation on secondary CPUs which causes issues on PREEMPT_RT.
> diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
> index 860eeb03cfe5d..31591c9472548 100644
> --- a/arch/arm/kernel/cacheinfo.c
> +++ b/arch/arm/kernel/cacheinfo.c
[ ... ]
> @@ -146,6 +139,11 @@ int init_cache_level(unsigned int cpu)
> return 0;
> }
>
> +int early_cache_level(unsigned int cpu)
> +{
> + return init_cache_level(cpu);
> +}
[Severity: High]
This is a pre-existing issue, but does this CLIDR fallback path still leave
the kernel vulnerable to a panic on PREEMPT_RT if a secondary CPU happens to
have more cache leaves than the boot CPU?
On big.LITTLE ARM systems, if a secondary CPU has more cache leaves and no
device-tree cache specification is used, early_leaves will be smaller than
the required size. This forces a reallocation while interrupts are disabled
on the secondary CPU:
arch/arm/kernel/smp.c:secondary_start_kernel() {
...
trace_hardirqs_off();
...
update_siblings_masks(cpu);
}
drivers/base/arch_topology.c:update_siblings_masks() {
...
ret = detect_cache_attributes(cpuid);
}
drivers/base/cacheinfo.c:init_level_allocate_ci() {
...
if (cache_leaves(cpu) <= early_leaves && per_cpu_cacheinfo(cpu))
return 0;
kfree(per_cpu_cacheinfo(cpu));
return allocate_cache_info(cpu);
}
drivers/base/cacheinfo.c:allocate_cache_info() {
...
per_cpu_cacheinfo(cpu) = kzalloc_objs(struct cacheinfo,
cache_leaves(cpu), GFP_ATOMIC);
}
On PREEMPT_RT, memory allocation with GFP_ATOMIC relies on a sleepable
slab rt_mutex. Calling this with interrupts disabled results in a BUG
("sleeping function called from invalid context") and a system panic
during boot.
Could this reallocation path be avoided or deferred to a context where
interrupts are enabled for secondary CPUs?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260912195552.76673-1-kmehltretter@gmail.com?part=2
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 3/4] ARM: cacheinfo: guard the CLIDR read in populate_cache_leaves()
2026-09-12 19:55 [PATCH 0/4] ARM: allocate the cacheinfo early to fix the PREEMPT_RT boot warning Karl Mehltretter
2026-09-12 19:55 ` [PATCH 1/4] ARM: cacheinfo: avoid out-of-bounds write in populate_cache_leaves() Karl Mehltretter
2026-09-12 19:55 ` [PATCH 2/4] ARM: cacheinfo: count external caches in early_cache_level() Karl Mehltretter
@ 2026-09-12 19:55 ` Karl Mehltretter
2026-09-12 19:55 ` [PATCH 4/4] ARM: topology: allocate the cacheinfo early on the boot CPU Karl Mehltretter
3 siblings, 0 replies; 6+ messages in thread
From: Karl Mehltretter @ 2026-09-12 19:55 UTC (permalink / raw)
To: Russell King, Dmitry Baryshkov, Sudeep Holla
Cc: Karl Mehltretter, Pierre Gondois, Linus Walleij, Radu Rendec,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
linux-arm-kernel, linux-rt-devel, linux-kernel, stable
ARM1176 and ARM11 MPCore can be reported as ARMv7 by
cpu_architecture() even though they lack CLIDR. populate_cache_leaves()
therefore needs the CTR-format check used by detect_cache_level().
DT-based early allocation bypasses init_cache_level(), so its check
no longer protects populate_cache_leaves(). A combined ARMv6/ARMv7
SMP kernel can reach this path on BCM2835, whose DT describes its
caches.
Share the CLIDR capability check between detection and population.
Return -ENOENT from population when CLIDR is unavailable, preserving
the existing absence of cacheinfo and avoiding a new topology warning.
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
Reproduced on QEMU realview-eb-mpcore (ARM11 MPCore, reported as ARMv7
by cpu_architecture()) with a device tree carrying i-cache-size and
d-cache-size on the cpu nodes like bcm2835.dtsi: with patch 4 and
without this patch populate_cache_leaves() reads CLIDR, which QEMU
returns as zero, so every leaf becomes CACHE_TYPE_NOCACHE. Real ARM11
does not implement the register. With this patch the read is skipped,
the boot is silent and cacheinfo stays absent as before the series.
arch/arm/kernel/cacheinfo.c | 25 +++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
index 31591c947254..993c8a134786 100644
--- a/arch/arm/kernel/cacheinfo.c
+++ b/arch/arm/kernel/cacheinfo.c
@@ -80,19 +80,21 @@ static void ci_leaf_init(struct cacheinfo *this_leaf,
this_leaf->type = type;
}
-static int detect_cache_level(unsigned int *level_p, unsigned int *leaves_p)
+static bool clidr_present(void)
{
- unsigned int ctype, level, leaves;
- u32 ctr, format;
-
/* CLIDR is not present before ARMv7/v7m */
if (cpu_architecture() < CPU_ARCH_ARMv7)
- return -EOPNOTSUPP;
+ return false;
/* Don't try reading CLIDR if CTR declares old format */
- ctr = read_cpuid_cachetype();
- format = FIELD_GET(CTR_FORMAT_MASK, ctr);
- if (format != CTR_FORMAT_ARMV7)
+ return FIELD_GET(CTR_FORMAT_MASK, read_cpuid_cachetype()) == CTR_FORMAT_ARMV7;
+}
+
+static int detect_cache_level(unsigned int *level_p, unsigned int *leaves_p)
+{
+ unsigned int ctype, level, leaves;
+
+ if (!clidr_present())
return -EOPNOTSUPP;
for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) {
@@ -150,11 +152,10 @@ int populate_cache_leaves(unsigned int cpu)
enum cache_type type;
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
struct cacheinfo *infos = this_cpu_ci->info_list;
- unsigned int arch = cpu_architecture();
- /* CLIDR is not present before ARMv7/v7m */
- if (arch < CPU_ARCH_ARMv7)
- return -EOPNOTSUPP;
+ /* The device tree can describe caches CLIDR cannot fill in. */
+ if (!clidr_present())
+ return -ENOENT;
for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
idx < this_cpu_ci->num_leaves; level++) {
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 4/4] ARM: topology: allocate the cacheinfo early on the boot CPU
2026-09-12 19:55 [PATCH 0/4] ARM: allocate the cacheinfo early to fix the PREEMPT_RT boot warning Karl Mehltretter
` (2 preceding siblings ...)
2026-09-12 19:55 ` [PATCH 3/4] ARM: cacheinfo: guard the CLIDR read in populate_cache_leaves() Karl Mehltretter
@ 2026-09-12 19:55 ` Karl Mehltretter
3 siblings, 0 replies; 6+ messages in thread
From: Karl Mehltretter @ 2026-09-12 19:55 UTC (permalink / raw)
To: Russell King, Dmitry Baryshkov, Sudeep Holla
Cc: Karl Mehltretter, Pierre Gondois, Linus Walleij, Radu Rendec,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
linux-arm-kernel, linux-rt-devel, linux-kernel, stable
ARM secondary CPUs allocate cacheinfo in detect_cache_attributes(),
called from secondary_start_kernel() with interrupts disabled. With
PREEMPT_RT, the allocation takes a sleeping lock and triggers
"BUG: sleeping function called from invalid context".
Call fetch_cache_info() for each possible CPU from init_cpu_topology()
on the boot CPU, following commit 5944ce092b97 ("arch_topology: Build
cacheinfo from primary CPU"). This supplies the arrays before secondary
CPUs populate them. Suppress -ENOENT and -EOPNOTSUPP, which leave cache
detection to the existing late path.
The CLIDR fallback uses the boot CPU's cache geometry. A secondary CPU
with more cache leaves can still require late reallocation and trigger
the warning.
Fixes: a9ff94477836 ("ARM: 9433/2: implement cacheinfo support")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
Full warning on QEMU virt, cortex-a15, 7.3-rc2, PREEMPT_RT:
BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48
in_atomic(): 1, irqs_disabled(): 128, non_block: 0, pid: 0, name: swapper/1
Call trace:
rt_spin_lock from ___slab_alloc+0x68/0x728
...
__kmalloc_noprof from detect_cache_attributes+0xe4/0x734
detect_cache_attributes from update_siblings_masks+0x10/0x174
update_siblings_masks from secondary_start_kernel+0xec/0x120
PREEMPT_RT became selectable on ARM in 7.1 with commit c6e61c06d606
("ARM: 9463/1: Allow to enable RT"). The PREEMPT_RT patch queues carry
that change for 6.18 and later, so 6.18.y is affected as well.
QEMU virt, cortex-a15, PREEMPT_RT, 2 and 4 CPUs: the warning is gone,
the cacheinfo sysfs tree is identical before and after, CPU 1 survives
an offline/online cycle. Same on a non-RT build.
QEMU realview-eb-mpcore, ARM11 MPCore, 4 CPUs, ARMv6 plus ARMv7 SMP
kernel with PREEMPT_RT: fetch_cache_info() returns -EOPNOTSUPP through
the CTR format check, no message, no cacheinfo before or after, CPU
hotplug works, kernel messages otherwise identical.
Raspberry Pi 400, four Cortex-A72 CPUs, 32-bit PREEMPT_RT 7.2.6-rc1,
full series: the boot warning is absent and all 12 cache sysfs entries
match the unpatched baseline and earlier topology-only run. The live
DT describes the shared L2, exercising DT-based early allocation.
CPU hotplug is unavailable on this platform.
arch/arm/kernel/topology.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 2336ee2aa44a..e6b50cd43ce5 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -12,6 +12,7 @@
*/
#include <linux/arch_topology.h>
+#include <linux/cacheinfo.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
@@ -238,8 +239,20 @@ void store_cpu_topology(unsigned int cpuid)
*/
void __init init_cpu_topology(void)
{
+ int cpu, ret;
+
reset_cpu_topology();
smp_wmb();
parse_dt_topology();
+
+ for_each_possible_cpu(cpu) {
+ ret = fetch_cache_info(cpu);
+ if (!ret)
+ continue;
+ /* CPUs without a usable CLIDR return -EOPNOTSUPP. */
+ if (ret != -ENOENT && ret != -EOPNOTSUPP)
+ pr_err("Early cacheinfo failed, ret = %d\n", ret);
+ return;
+ }
}
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread