Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 91e13aa3 authored by Aaron Lu's avatar Aaron Lu Committed by Rafael J. Wysocki
Browse files

ACPI: video: correct acpi_video_bus_add error processing

acpi_video_bus_get_devices() may fail due to some video output device
doesn't have the _ADR method, and in this case, the error processing
is to simply free the video structure in acpi_video_bus_add(), while
leaving those already registered video output devices in the wild,
which means for some video output device, we have already registered
a backlight interface and installed a notification handler for it.
So it can happen when user is using this system, on hotkey pressing,
the notification handler will send a keycode through a non-existing
input device, causing kernel freeze.

To solve this problem, free all those already registered video output
devices once something goes wrong in acpi_video_bus_get_devices(), so
that no wild backlight interfaces and notification handlers exist.

References: https://bugzilla.kernel.org/show_bug.cgi?id=51731


Reported-and-tested-by: default avatar <i-tek@web.de>
Signed-off-by: default avatarAaron Lu <aaron.lu@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent a6432ded
Loading
Loading
Loading
Loading
+66 −77
Original line number Diff line number Diff line
@@ -167,7 +167,8 @@ struct acpi_video_device_flags {
	u8 dvi:1;
	u8 bios:1;
	u8 unknown:1;
	u8 reserved:2;
	u8 notify:1;
	u8 reserved:1;
};

struct acpi_video_device_cap {
@@ -1074,12 +1075,11 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
	struct acpi_video_device *data;
	struct acpi_video_device_attrib* attribute;

	if (!device || !video)
		return -EINVAL;

	status =
	    acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
	if (ACPI_SUCCESS(status)) {
	/* Some device omits _ADR, we skip them instead of fail */
	if (ACPI_FAILURE(status))
		return 0;

	data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
	if (!data)
@@ -1117,8 +1117,7 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
			data->flags.bios = 1;
	} else {
		/* Check for legacy IDs */
			device_type = acpi_video_get_device_type(video,
								 device_id);
		device_type = acpi_video_get_device_type(video, device_id);
		/* Ignore bits 16 and 18-20 */
		switch (device_type & 0xffe2ffff) {
			case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR:
@@ -1138,28 +1137,18 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
	acpi_video_device_bind(video, data);
	acpi_video_device_find_cap(data);

		status = acpi_install_notify_handler(device->handle,
						     ACPI_DEVICE_NOTIFY,
						     acpi_video_device_notify,
						     data);
		if (ACPI_FAILURE(status)) {
			printk(KERN_ERR PREFIX
					  "Error installing notify handler\n");
			if(data->brightness)
				kfree(data->brightness->levels);
			kfree(data->brightness);
			kfree(data);
			return -ENODEV;
		}
	status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
					     acpi_video_device_notify, data);
	if (ACPI_FAILURE(status))
		dev_err(&device->dev, "Error installing notify handler\n");
	else
		data->flags.notify = 1;

	mutex_lock(&video->device_list_lock);
	list_add_tail(&data->entry, &video->video_device_list);
	mutex_unlock(&video->device_list_lock);

		return 0;
	}

	return -ENOENT;
	return status;
}

/*
@@ -1452,9 +1441,8 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video,

		status = acpi_video_bus_get_one_device(dev, video);
		if (status) {
			printk(KERN_WARNING PREFIX
					"Can't attach device\n");
			continue;
			dev_err(&dev->dev, "Can't attach device\n");
			break;
		}
	}
	return status;
@@ -1467,13 +1455,14 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
	if (!device || !device->video)
		return -ENOENT;

	if (device->flags.notify) {
		status = acpi_remove_notify_handler(device->dev->handle,
					    ACPI_DEVICE_NOTIFY,
					    acpi_video_device_notify);
	if (ACPI_FAILURE(status)) {
		printk(KERN_WARNING PREFIX
				ACPI_DEVICE_NOTIFY, acpi_video_device_notify);
		if (ACPI_FAILURE(status))
			dev_err(&device->dev->dev,
					"Can't remove video notify handler\n");
	}

	if (device->backlight) {
		backlight_device_unregister(device->backlight);
		device->backlight = NULL;
@@ -1755,7 +1744,7 @@ static int acpi_video_bus_add(struct acpi_device *device)

	error = acpi_video_bus_get_devices(video, device);
	if (error)
		goto err_free_video;
		goto err_put_video;

	video->input = input = input_allocate_device();
	if (!input) {