PCI/ACPI: Validate acpi_pci_set_power_state() parameter
authorBjorn Helgaas <bhelgaas@google.com>
Wed, 21 Jun 2023 21:36:12 +0000 (16:36 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Jul 2023 17:27:37 +0000 (18:27 +0100)
commit 5557b62634abbd55bab7b154ce4bca348ad7f96f upstream.

Previously acpi_pci_set_power_state() assumed the requested power state was
valid (PCI_D0 ... PCI_D3cold).  If a caller supplied something else, we
could index outside the state_conv[] array and pass junk to
acpi_device_set_power().

Validate the pci_power_t parameter and return -EINVAL if it's invalid.

Link: https://lore.kernel.org/r/20230621222857.GA122930@bhelgaas
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/pci/pci-acpi.c

index 1698205..e6bf960 100644 (file)
@@ -1053,32 +1053,37 @@ int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
                [PCI_D3hot] = ACPI_STATE_D3_HOT,
                [PCI_D3cold] = ACPI_STATE_D3_COLD,
        };
-       int error = -EINVAL;
+       int error;
 
        /* If the ACPI device has _EJ0, ignore the device */
        if (!adev || acpi_has_method(adev->handle, "_EJ0"))
                return -ENODEV;
 
        switch (state) {
-       case PCI_D3cold:
-               if (dev_pm_qos_flags(&dev->dev, PM_QOS_FLAG_NO_POWER_OFF) ==
-                               PM_QOS_FLAGS_ALL) {
-                       error = -EBUSY;
-                       break;
-               }
-               fallthrough;
        case PCI_D0:
        case PCI_D1:
        case PCI_D2:
        case PCI_D3hot:
-               error = acpi_device_set_power(adev, state_conv[state]);
+       case PCI_D3cold:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (state == PCI_D3cold) {
+               if (dev_pm_qos_flags(&dev->dev, PM_QOS_FLAG_NO_POWER_OFF) ==
+                               PM_QOS_FLAGS_ALL)
+                       return -EBUSY;
        }
 
-       if (!error)
-               pci_dbg(dev, "power state changed by ACPI to %s\n",
-                       acpi_power_state_string(adev->power.state));
+       error = acpi_device_set_power(adev, state_conv[state]);
+       if (error)
+               return error;
+
+       pci_dbg(dev, "power state changed by ACPI to %s\n",
+               acpi_power_state_string(adev->power.state));
 
-       return error;
+       return 0;
 }
 
 pci_power_t acpi_pci_get_power_state(struct pci_dev *dev)