[PATCH] ACPI: lpat: Avoid division by zero on degenerate LPAT tables
From: shao.mingyin
Date: Sat Nov 07 2026 - 08:01:15 EST
From: Shao Mingyin <shao.mingyin@xxxxxxxxxx>
acpi_lpat_raw_to_temp() and acpi_lpat_temp_to_raw() linearly interpolate
between consecutive LPAT table entries by dividing the difference of the
other coordinate by delta_raw or delta_temp. The table is provided by
the firmware through the LPAT control method and
acpi_lpat_get_conversion_table() only checks the package shape and the
element types, so nothing guarantees that two adjacent entries have
distinct raw (or temperature) values.
If the queried value lies between two consecutive entries that share the
same raw value, delta_raw is zero and acpi_lpat_raw_to_temp() traps with
a #DE exception and oopses the kernel. The same happens in
acpi_lpat_temp_to_raw() when two adjacent entries share the same
temperature. Callers such as int340x thermal zones and the Intel PMIC
opregion handlers pass firmware-controlled values straight into these
functions, so a malformed LPAT object is enough to trigger the trap.
Return the endpoint value instead of dividing when the delta is zero. In
that case the query value necessarily equals the shared endpoint, so the
other coordinate of that entry is the natural fallback and no interpolation
is possible.
Fixes: c55d62820efe ("ACPI / LPAT: Common table processing functions")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Shao Mingyin <shao.mingyin@xxxxxxxxxx>
---
Tested on 7.3.0-next-20261030 (QEMU/TCG guest) with a TEST-ONLY hook and
initramfs, booted with oops=panic. QEMU has no Intel PMIC or int340x
thermal device model, so the hook fabricates degenerate LPAT tables and
calls the real acpi_lpat_raw_to_temp()/acpi_lpat_temp_to_raw() at
late_initcall time; it is applied to both kernels and is not part of this
patch. A normal interpolation runs first as a control, and lpat_test= on
the kernel command line selects one degenerate case per boot so that the
unpatched kernel reaches both:
unpatched (two boots) patched (one boot)
raw_to_temp(50) = 200 raw_to_temp(50) = 200
lpat_test=raw: raw_to_temp(50) = 300
Oops: divide error, RIP temp_to_raw(300) = 10
acpi_lpat_raw_to_temp, panic
lpat_test=temp:
Oops: divide error, RIP
acpi_lpat_temp_to_raw, panic
diff --git a/drivers/acpi/acpi_lpat.c b/drivers/acpi/acpi_lpat.c
index 6141c9070eca..9090a83e0806 100644
--- a/drivers/acpi/acpi_lpat.c
+++ b/drivers/acpi/acpi_lpat.c
@@ -37,6 +37,8 @@ int acpi_lpat_raw_to_temp(struct acpi_lpat_conversion_table *lpat_table,
delta_temp = lpat[i+1].temp - lpat[i].temp;
delta_raw = lpat[i+1].raw - lpat[i].raw;
+ if (!delta_raw)
+ return lpat[i].temp;
temp = lpat[i].temp + (raw - lpat[i].raw) * delta_temp / delta_raw;
return temp;
@@ -70,6 +72,8 @@ int acpi_lpat_temp_to_raw(struct acpi_lpat_conversion_table *lpat_table,
delta_temp = lpat[i+1].temp - lpat[i].temp;
delta_raw = lpat[i+1].raw - lpat[i].raw;
+ if (!delta_temp)
+ return lpat[i].raw;
raw = lpat[i].raw + (temp - lpat[i].temp) * delta_raw / delta_temp;
return raw;