* [PATCH v2 1/3] irqchip/gic-v3-its: Don't free a vPE table shared with another ITS
2026-09-25 8:49 [PATCH v2 0/3] irqchip/gic-v3: Memory-safety fixes on the probe paths Fuad Tabba
@ 2026-09-25 8:49 ` Fuad Tabba
2026-09-25 8:49 ` [PATCH v2 2/3] irqchip/gic-v3-its: Don't free the tables of an enabled ITS Fuad Tabba
2026-09-25 8:50 ` [PATCH v2 3/3] irqchip/gic-v3: Don't register a redistributor that was never counted Fuad Tabba
2 siblings, 0 replies; 4+ messages in thread
From: Fuad Tabba @ 2026-09-25 8:49 UTC (permalink / raw)
To: Marc Zyngier, Thomas Gleixner
Cc: Radu Rendec, James Morse, Will Deacon, Fuad Tabba,
linux-arm-kernel, linux-kernel
On GICv4.1, its_alloc_tables() copies a sibling's vPE table baser
rather than allocating one, but its_free_tables() frees it like the
rest. A probe failure on the copying ITS then frees pages the enabled
sibling still points GITS_BASER2 at, and the kernel comes up running
on that sibling.
Mark a shared table so its_free_tables() skips it.
Fixes: 5e5168461c22c ("irqchip/gic-v4.1: VPE table (aka GICR_VPROPBASER) allocation")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
drivers/irqchip/irq-gic-v3-its.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index e9807af235373..58e52095e6ea8 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -87,6 +87,8 @@ struct its_baser {
u64 val;
u32 order;
u32 psz;
+ /* Inherited from a sibling ITS, not freed here */
+ bool shared;
};
struct its_device;
@@ -2604,7 +2606,8 @@ static void its_free_tables(struct its_node *its)
for (i = 0; i < GITS_BASER_NR_REGS; i++) {
if (its->tables[i].base) {
- its_free_pages(its->tables[i].base, its->tables[i].order);
+ if (!its->tables[i].shared)
+ its_free_pages(its->tables[i].base, its->tables[i].order);
its->tables[i].base = NULL;
}
}
@@ -2703,6 +2706,7 @@ static int its_alloc_tables(struct its_node *its)
WARN_ON(i != 2);
if ((sibling = find_sibling_its(its))) {
*baser = sibling->tables[2];
+ baser->shared = true;
its_write_baser(its, baser, baser->val);
continue;
}
--
2.39.5
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v2 2/3] irqchip/gic-v3-its: Don't free the tables of an enabled ITS
2026-09-25 8:49 [PATCH v2 0/3] irqchip/gic-v3: Memory-safety fixes on the probe paths Fuad Tabba
2026-09-25 8:49 ` [PATCH v2 1/3] irqchip/gic-v3-its: Don't free a vPE table shared with another ITS Fuad Tabba
@ 2026-09-25 8:49 ` Fuad Tabba
2026-09-25 8:50 ` [PATCH v2 3/3] irqchip/gic-v3: Don't register a redistributor that was never counted Fuad Tabba
2 siblings, 0 replies; 4+ messages in thread
From: Fuad Tabba @ 2026-09-25 8:49 UTC (permalink / raw)
To: Marc Zyngier, Thomas Gleixner
Cc: Radu Rendec, James Morse, Will Deacon, Fuad Tabba,
linux-arm-kernel, linux-kernel
its_probe_one() enables the ITS before its_init_domain(), the last step
that can fail, and the unwind from there frees the tables and the
command queue without disabling it. The kernel comes up with an enabled
ITS pointing at freed pages.
Enable the ITS last. Nothing reaches the new domain before then: this
runs from init_IRQ(), and the ITS joins its_nodes only after the enable.
Fixes: 4c21f3c26ecc2 ("irqchip: GICv3: ITS: DT probing and initialization")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
drivers/irqchip/irq-gic-v3-its.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 58e52095e6ea8..a5690721f0feb 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -5332,16 +5332,18 @@ static int __init its_probe_one(struct its_node *its)
}
gits_write_cwriter(0, its->base + GITS_CWRITER);
+
+ err = its_init_domain(its);
+ if (err)
+ goto out_free_collection;
+
+ /* Enable last: the unwind frees memory an enabled ITS may access */
ctlr = readl_relaxed(its->base + GITS_CTLR);
ctlr |= GITS_CTLR_ENABLE;
if (is_v4(its))
ctlr |= GITS_CTLR_ImDe;
writel_relaxed(ctlr, its->base + GITS_CTLR);
- err = its_init_domain(its);
- if (err)
- goto out_free_collection;
-
raw_spin_lock(&its_lock);
list_add(&its->entry, &its_nodes);
raw_spin_unlock(&its_lock);
--
2.39.5
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v2 3/3] irqchip/gic-v3: Don't register a redistributor that was never counted
2026-09-25 8:49 [PATCH v2 0/3] irqchip/gic-v3: Memory-safety fixes on the probe paths Fuad Tabba
2026-09-25 8:49 ` [PATCH v2 1/3] irqchip/gic-v3-its: Don't free a vPE table shared with another ITS Fuad Tabba
2026-09-25 8:49 ` [PATCH v2 2/3] irqchip/gic-v3-its: Don't free the tables of an enabled ITS Fuad Tabba
@ 2026-09-25 8:50 ` Fuad Tabba
2 siblings, 0 replies; 4+ messages in thread
From: Fuad Tabba @ 2026-09-25 8:50 UTC (permalink / raw)
To: Marc Zyngier, Thomas Gleixner
Cc: Radu Rendec, James Morse, Will Deacon, Fuad Tabba,
linux-arm-kernel, linux-kernel
gic_acpi_match_gicc() counts only the enabled GICCs with a non-zero
gicr_base_address, and that count sizes redist_regs[], but
gic_acpi_parse_madt_gicc() registers every enabled one. On a MADT with
no GICR entries, an enabled GICC with a zero GICR base therefore makes
gic_acpi_register_redist() write a struct redist_region past the end of
the array.
Commit fa2dabe57220e ("irqchip/gic-v3: Don't return errors from
gic_acpi_match_gicc()") removed the check that kept the two consistent.
Its message says such entries are still caught by gic_populate_rdist(),
but that runs from gic_cpu_init(), after the write.
Skip the entry instead, and mark the CPU's redistributor broken as the
online-capable path does, so that gic_check_rdist() rejects the CPU.
gic_starting_cpu() runs past cpuhp's last failure point, so without the
mark the CPU would come online with its redistributor and CPU interface
unconfigured. With an ITS, its_cpu_init() would then dereference a NULL
rd_base.
Fixes: fa2dabe57220e ("irqchip/gic-v3: Don't return errors from gic_acpi_match_gicc()")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
drivers/irqchip/irq-gic-v3.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 6e1fa5b247fc4..bfc10d657fd57 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -2344,6 +2344,17 @@ gic_acpi_parse_madt_gicc(union acpi_subtable_headers *header,
return 0;
}
+ /* Not counted by gic_acpi_match_gicc(), so there is no slot for it */
+ if (!gicc->gicr_base_address) {
+ int cpu = get_cpu_for_acpi_id(gicc->uid);
+
+ pr_warn(FW_BUG "GICC entry with ACPI UID %u has no GICR base address, CPU kept offline\n",
+ gicc->uid);
+ if (cpu >= 0)
+ cpumask_set_cpu(cpu, &broken_rdists);
+ return 0;
+ }
+
redist_base = ioremap(gicc->gicr_base_address, size);
if (!redist_base)
return -ENOMEM;
--
2.39.5
^ permalink raw reply [flat|nested] 4+ messages in thread