summaryrefslogtreecommitdiffstats
path: root/drivers/acpi/device_pm.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2022-08-29 17:21:43 +0200
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2022-08-30 14:22:51 +0200
commitd5008ef5b5a21177d3042816542f1afd5ae36152 (patch)
treec5d2b05e6e91a93f7b74db9a1f3d2abbc187cb72 /drivers/acpi/device_pm.c
parentACPI: Drop parent field from struct acpi_device (diff)
downloadlinux-d5008ef5b5a21177d3042816542f1afd5ae36152.tar.xz
linux-d5008ef5b5a21177d3042816542f1afd5ae36152.zip
ACPI: PM: Fix NULL argument handling in acpi_device_get/set_power()
In principle, it should be valid to pass NULL as the ACPI device pointer to acpi_device_get_power() and acpi_device_set_power() and they both are expected to return -EINVAL in that case, but that has been broken recently by commit 62fcb99bdf10 ("ACPI: Drop parent field from struct acpi_device") which has caused the ACPI device pointer to be dereferenced in these functions before the NULL check. Fix that and while at it make acpi_device_set_power() only use the parent field if the target ACPI device object's ignore_parent flag in not set. Fixes: 62fcb99bdf10 ("ACPI: Drop parent field from struct acpi_device") Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/device_pm.c')
-rw-r--r--drivers/acpi/device_pm.c23
1 files changed, 14 insertions, 9 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index 028d8d14cd44..3aca67cf9ce5 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -74,13 +74,15 @@ static int acpi_dev_pm_explicit_get(struct acpi_device *device, int *state)
*/
int acpi_device_get_power(struct acpi_device *device, int *state)
{
- struct acpi_device *parent = acpi_dev_parent(device);
int result = ACPI_STATE_UNKNOWN;
+ struct acpi_device *parent;
int error;
if (!device || !state)
return -EINVAL;
+ parent = acpi_dev_parent(device);
+
if (!device->flags.power_manageable) {
/* TBD: Non-recursive algorithm for walking up hierarchy. */
*state = parent ? parent->power.state : ACPI_STATE_D0;
@@ -159,7 +161,6 @@ static int acpi_dev_pm_explicit_set(struct acpi_device *adev, int state)
*/
int acpi_device_set_power(struct acpi_device *device, int state)
{
- struct acpi_device *parent = acpi_dev_parent(device);
int target_state = state;
int result = 0;
@@ -192,13 +193,17 @@ int acpi_device_set_power(struct acpi_device *device, int state)
return -ENODEV;
}
- if (!device->power.flags.ignore_parent && parent &&
- state < parent->power.state) {
- acpi_handle_debug(device->handle,
- "Cannot transition to %s for parent in %s\n",
- acpi_power_state_string(state),
- acpi_power_state_string(parent->power.state));
- return -ENODEV;
+ if (!device->power.flags.ignore_parent) {
+ struct acpi_device *parent;
+
+ parent = acpi_dev_parent(device);
+ if (parent && state < parent->power.state) {
+ acpi_handle_debug(device->handle,
+ "Cannot transition to %s for parent in %s\n",
+ acpi_power_state_string(state),
+ acpi_power_state_string(parent->power.state));
+ return -ENODEV;
+ }
}
/*