diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2022-08-29 17:21:43 +0200 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2022-08-30 14:22:51 +0200 |
commit | d5008ef5b5a21177d3042816542f1afd5ae36152 (patch) | |
tree | c5d2b05e6e91a93f7b74db9a1f3d2abbc187cb72 | |
parent | ACPI: Drop parent field from struct acpi_device (diff) | |
download | linux-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>
-rw-r--r-- | drivers/acpi/device_pm.c | 23 |
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; + } } /* |