Commit d5008ef5 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

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 62fcb99b ("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: 62fcb99b ("ACPI: Drop parent field from struct acpi_device")
Reported-by: default avatarDan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 62fcb99b
Loading
Loading
Loading
Loading
+14 −9
Original line number Diff line number Diff line
@@ -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,14 +193,18 @@ int acpi_device_set_power(struct acpi_device *device, int state)
		return -ENODEV;
	}

	if (!device->power.flags.ignore_parent && parent &&
	    state < parent->power.state) {
	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;
		}
	}

	/*
	 * Transition Power