load_late_stop_cpus() snapshots CPU capabilities before a late microcode
update, while microcode_check() snapshots the capabilities again after the
update.
Both functions allocate their struct cpuinfo_x86 snapshots on the stack.
Meanwhile, cpuinfo_x86 contains the parsed CPUID tables where more leaves
will be added; resulting in "stack frame length exceeded" warnings.
Allocate the before/after cpuinfo_x86 snapshots on the heap.
For load_late_stop_cpus(), do the memory allocation before the microcode
staging step. This leaves no leaked or stale microcode state in -ENOMEM
failure modes.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
---
arch/x86/kernel/cpu/common.c | 11 ++++++++---
arch/x86/kernel/cpu/microcode/core.c | 9 ++++++---
2 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 2beb53f6bed7..ece5a59124f5 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -2543,15 +2543,20 @@ void store_cpu_caps(struct cpuinfo_x86 *curr_info)
*/
void microcode_check(struct cpuinfo_x86 *prev_info)
{
- struct cpuinfo_x86 curr_info;
+ struct cpuinfo_x86 *curr_info __free(kfree) = kmalloc_obj(*curr_info);
perf_check_microcode();
amd_check_microcode();
- store_cpu_caps(&curr_info);
+ if (!curr_info) {
+ pr_warn("x86/CPU: Microcode update CPU capability changes check was skipped (ENOMEM)\n");
+ return;
+ }
+
+ store_cpu_caps(curr_info);
- if (!memcmp(&prev_info->x86_capability, &curr_info.x86_capability,
+ if (!memcmp(&prev_info->x86_capability, &curr_info->x86_capability,
sizeof(prev_info->x86_capability)))
return;
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index 56d791aeac4e..7a00671540bc 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -588,16 +588,19 @@ static int load_cpus_stopped(void *unused)
static int load_late_stop_cpus(bool is_safe)
{
+ struct cpuinfo_x86 *prev_info __free(kfree) = kmalloc_obj(*prev_info);
unsigned int cpu, updated = 0, failed = 0, timedout = 0, siblings = 0;
unsigned int nr_offl, offline = 0;
int old_rev = boot_cpu_data.microcode;
- struct cpuinfo_x86 prev_info;
if (!is_safe) {
pr_err("Late microcode loading without minimal revision check.\n");
pr_err("You should switch to early loading, if possible.\n");
}
+ if (!prev_info)
+ return -ENOMEM;
+
/*
* Pre-load the microcode image into a staging device. This
* process is preemptible and does not require stopping CPUs.
@@ -617,7 +620,7 @@ static int load_late_stop_cpus(bool is_safe)
* Take a snapshot before the microcode update in order to compare and
* check whether any bits changed after an update.
*/
- store_cpu_caps(&prev_info);
+ store_cpu_caps(prev_info);
if (microcode_ops->use_nmi)
static_branch_enable_cpuslocked(µcode_nmi_handler_enable);
@@ -666,7 +669,7 @@ static int load_late_stop_cpus(bool is_safe)
num_online_cpus() - (updated + siblings));
}
pr_info("revision: 0x%x -> 0x%x\n", old_rev, boot_cpu_data.microcode);
- microcode_check(&prev_info);
+ microcode_check(prev_info);
return updated + siblings == num_online_cpus() ? 0 : -EIO;
}
--
2.53.0