ACPI: APEI: GHES: Bound AER info copy and sanitize software metadata

ghes_handle_aer() copies sizeof(struct aer_capability_regs) out of the
fixed 96-byte pcie_err->aer_info. The struct is larger, so the copy reads
past the section, and it fills the software-only header_len and flit fields
of the embedded struct pcie_tlp_log from firmware bytes.
pcie_print_tlp_log() uses both to bound a loop over dw[], so a large value
walks past the array. Nothing checks the section can hold a struct
cper_sec_pcie either.

Validate error_data_length, zero the destination, and map aer_info onto the
struct as extlog_print_pcie() does: copy up to the four Header Log DWORDs,
then place the TLP Prefix Log from its own offset. The rest stays zero,
covering header_len and flit.

Reported-by: sashiko-bot@kernel.org
Closes: https://sashiko.dev/#/patchset/20260714231835.303081-1-dave.jiang@intel.com?part=3
Fixes: 7e077e6707 ("PCI/ERR: Handle TLP Log in Flit mode")
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Shuai Xue <xueshuai@linux.alibaba.com>
Reviewed-by: Hanjun Guo <guohanjun@huawei.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Link: https://patch.msgid.link/20260904172337.1409775-13-dave.jiang@intel.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Dave Jiang 2026-09-04 10:23:36 -07:00 committed by Rafael J. Wysocki
parent 10338c9085
commit 9c3ad3bfff

View File

@ -642,11 +642,14 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata)
#ifdef CONFIG_ACPI_APEI_PCIEAER
struct cper_sec_pcie *pcie_err = acpi_hest_get_payload(gdata);
if (gdata->error_data_length < sizeof(*pcie_err))
return;
if (pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID &&
pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) {
struct aer_capability_regs *aer_info;
unsigned int devfn;
int aer_severity;
u8 *aer_info;
devfn = PCI_DEVFN(pcie_err->device_id.device,
pcie_err->device_id.function);
@ -664,13 +667,25 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata)
sizeof(struct aer_capability_regs));
if (!aer_info)
return;
memcpy(aer_info, pcie_err->aer_info, sizeof(struct aer_capability_regs));
/*
* Map aer_info onto the struct as extlog_print_pcie() does:
* copy up to the four Header Log DWORDs, then place the TLP
* Prefix Log from where the hardware keeps it. The rest stays
* zero, so firmware cannot drive the pcie_print_tlp_log() loop
* over dw[] out of bounds.
*/
memset(aer_info, 0, sizeof(struct aer_capability_regs));
memcpy(aer_info, pcie_err->aer_info,
offsetof(struct aer_capability_regs, header_log) +
PCIE_STD_NUM_TLP_HEADERLOG * sizeof(u32));
memcpy(aer_info->header_log.prefix,
pcie_err->aer_info + PCI_ERR_PREFIX_LOG,
sizeof(aer_info->header_log.prefix));
aer_recover_queue(pcie_err->device_id.segment,
pcie_err->device_id.bus,
devfn, aer_severity,
(struct aer_capability_regs *)
aer_info);
devfn, aer_severity, aer_info);
}
#endif
}