aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorRoss Lagerwall <ross.lagerwall@citrix.com>2019-01-28 05:04:24 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2019-02-20 04:34:35 -0500
commit45b14a4ffcc1e0b5caa246638f942cbe7eaea7ad (patch)
tree95147717a2c03aea85435616966bdb5a5db8e52f /drivers/firmware
parent1c0d9b1c31d19d3aa90296a184e4624545d7d1ee (diff)
efi: cper: Fix possible out-of-bounds access
When checking a generic status block, we iterate over all the generic data blocks. The loop condition only checks that the start of the generic data block is valid (within estatus->data_length) but not the whole block. Because the size of data blocks (excluding error data) may vary depending on the revision and the revision is contained within the data block, ensure that enough of the current data block is valid before dereferencing any members otherwise an out-of-bounds access may occur if estatus->data_length is invalid. This relies on the fact that struct acpi_hest_generic_data_v300 is a superset of the earlier version. Also rework the other checks to avoid potential underflow. Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com> Acked-by: Borislav Petkov <bp@suse.de> Tested-by: Tyler Baicar <baicar.tyler@gmail.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/efi/cper.c13
1 files changed, 9 insertions, 4 deletions
diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c
index a7902fccdcfa..6090d25dce85 100644
--- a/drivers/firmware/efi/cper.c
+++ b/drivers/firmware/efi/cper.c
@@ -546,19 +546,24 @@ EXPORT_SYMBOL_GPL(cper_estatus_check_header);
546int cper_estatus_check(const struct acpi_hest_generic_status *estatus) 546int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
547{ 547{
548 struct acpi_hest_generic_data *gdata; 548 struct acpi_hest_generic_data *gdata;
549 unsigned int data_len, gedata_len; 549 unsigned int data_len, record_size;
550 int rc; 550 int rc;
551 551
552 rc = cper_estatus_check_header(estatus); 552 rc = cper_estatus_check_header(estatus);
553 if (rc) 553 if (rc)
554 return rc; 554 return rc;
555
555 data_len = estatus->data_length; 556 data_len = estatus->data_length;
556 557
557 apei_estatus_for_each_section(estatus, gdata) { 558 apei_estatus_for_each_section(estatus, gdata) {
558 gedata_len = acpi_hest_get_error_length(gdata); 559 if (sizeof(struct acpi_hest_generic_data) > data_len)
559 if (gedata_len > data_len - acpi_hest_get_size(gdata)) 560 return -EINVAL;
561
562 record_size = acpi_hest_get_record_size(gdata);
563 if (record_size > data_len)
560 return -EINVAL; 564 return -EINVAL;
561 data_len -= acpi_hest_get_record_size(gdata); 565
566 data_len -= record_size;
562 } 567 }
563 if (data_len) 568 if (data_len)
564 return -EINVAL; 569 return -EINVAL;