From 2949ad50711cc161721cf788711722eeeca33764 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Sat, 19 Feb 2011 14:18:08 +0100 Subject: ACPI / debugfs: Fix buffer overflows, double free File position is not controlled, it may lead to overwrites of arbitrary kernel memory. Also the code may kfree() the same pointer multiple times. One more flaw is still present: if multiple processes open the file then all 3 static variables are shared, leading to various race conditions. They should be moved to file->private_data. Signed-off-by: Vasiliy Kulikov Reviewed-by: WANG Cong Reviewed-by: Eugene Teo Cc: stable@kernel.org Signed-off-by: Rafael J. Wysocki --- drivers/acpi/debugfs.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/debugfs.c b/drivers/acpi/debugfs.c index 5df67f1d6c6..384f7abcff7 100644 --- a/drivers/acpi/debugfs.c +++ b/drivers/acpi/debugfs.c @@ -26,7 +26,9 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, size_t count, loff_t *ppos) { static char *buf; - static int uncopied_bytes; + static u32 max_size; + static u32 uncopied_bytes; + struct acpi_table_header table; acpi_status status; @@ -37,19 +39,24 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, if (copy_from_user(&table, user_buf, sizeof(struct acpi_table_header))) return -EFAULT; - uncopied_bytes = table.length; - buf = kzalloc(uncopied_bytes, GFP_KERNEL); + uncopied_bytes = max_size = table.length; + buf = kzalloc(max_size, GFP_KERNEL); if (!buf) return -ENOMEM; } - if (uncopied_bytes < count) { - kfree(buf); + if (buf == NULL) + return -EINVAL; + + if ((*ppos > max_size) || + (*ppos + count > max_size) || + (*ppos + count < count) || + (count > uncopied_bytes)) return -EINVAL; - } if (copy_from_user(buf + (*ppos), user_buf, count)) { kfree(buf); + buf = NULL; return -EFAULT; } @@ -59,6 +66,7 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, if (!uncopied_bytes) { status = acpi_install_method(buf); kfree(buf); + buf = NULL; if (ACPI_FAILURE(status)) return -EINVAL; add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); -- cgit v1.2.3 From 981858bd7a401aa9607d9f430d5de920025fc3ea Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 24 Feb 2011 19:59:21 +0100 Subject: ACPI / ACPICA: Implicit notify for multiple devices Commit bba63a2 (ACPICA: Implicit notify support) introduced a mechanism that causes a notify request of type ACPI_NOTIFY_DEVICE_WAKE to be queued automatically by acpi_ev_asynch_execute_gpe_method() for the device whose _PRW points to the GPE being handled if that GPE is not associated with an _Lxx/_Exx method. However, it turns out that on some systems there are multiple devices with _PRW pointing to the same GPE without _Lxx/_Exx and the mechanism introduced by commit bba63a2 needs to be extended so that "implicit" notify requests of type ACPI_NOTIFY_DEVICE_WAKE can be queued automatically for all those devices at the same time. Reported-and-tested-by: Matthew Garrett Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/aclocal.h | 7 ++++++- drivers/acpi/acpica/evgpe.c | 17 +++++++++++++---- drivers/acpi/acpica/evxfgpe.c | 42 +++++++++++++++++++++++++++++++++--------- 3 files changed, 52 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 54784bb42ce..edc25867ad9 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -416,10 +416,15 @@ struct acpi_gpe_handler_info { u8 originally_enabled; /* True if GPE was originally enabled */ }; +struct acpi_gpe_notify_object { + struct acpi_namespace_node *node; + struct acpi_gpe_notify_object *next; +}; + union acpi_gpe_dispatch_info { struct acpi_namespace_node *method_node; /* Method node for this GPE level */ struct acpi_gpe_handler_info *handler; /* Installed GPE handler */ - struct acpi_namespace_node *device_node; /* Parent _PRW device for implicit notify */ + struct acpi_gpe_notify_object device; /* List of _PRW devices for implicit notify */ }; /* diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 14988a86066..f4725212eb4 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -457,6 +457,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) acpi_status status; struct acpi_gpe_event_info *local_gpe_event_info; struct acpi_evaluate_info *info; + struct acpi_gpe_notify_object *notify_object; ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method); @@ -508,10 +509,18 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) * from this thread -- because handlers may in turn run other * control methods. */ - status = - acpi_ev_queue_notify_request(local_gpe_event_info->dispatch. - device_node, - ACPI_NOTIFY_DEVICE_WAKE); + status = acpi_ev_queue_notify_request( + local_gpe_event_info->dispatch.device.node, + ACPI_NOTIFY_DEVICE_WAKE); + + notify_object = local_gpe_event_info->dispatch.device.next; + while (ACPI_SUCCESS(status) && notify_object) { + status = acpi_ev_queue_notify_request( + notify_object->node, + ACPI_NOTIFY_DEVICE_WAKE); + notify_object = notify_object->next; + } + break; case ACPI_GPE_DISPATCH_METHOD: diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 3b20a3401b6..52aaff3df56 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -198,7 +198,9 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device, acpi_status status = AE_BAD_PARAMETER; struct acpi_gpe_event_info *gpe_event_info; struct acpi_namespace_node *device_node; + struct acpi_gpe_notify_object *notify_object; acpi_cpu_flags flags; + u8 gpe_dispatch_mask; ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake); @@ -221,27 +223,49 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device, goto unlock_and_exit; } + if (wake_device == ACPI_ROOT_OBJECT) { + goto out; + } + /* * If there is no method or handler for this GPE, then the * wake_device will be notified whenever this GPE fires (aka * "implicit notify") Note: The GPE is assumed to be * level-triggered (for windows compatibility). */ - if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_NONE) && (wake_device != ACPI_ROOT_OBJECT)) { + gpe_dispatch_mask = gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK; + if (gpe_dispatch_mask != ACPI_GPE_DISPATCH_NONE + && gpe_dispatch_mask != ACPI_GPE_DISPATCH_NOTIFY) { + goto out; + } - /* Validate wake_device is of type Device */ + /* Validate wake_device is of type Device */ - device_node = ACPI_CAST_PTR(struct acpi_namespace_node, - wake_device); - if (device_node->type != ACPI_TYPE_DEVICE) { - goto unlock_and_exit; - } + device_node = ACPI_CAST_PTR(struct acpi_namespace_node, wake_device); + if (device_node->type != ACPI_TYPE_DEVICE) { + goto unlock_and_exit; + } + + if (gpe_dispatch_mask == ACPI_GPE_DISPATCH_NONE) { gpe_event_info->flags = (ACPI_GPE_DISPATCH_NOTIFY | ACPI_GPE_LEVEL_TRIGGERED); - gpe_event_info->dispatch.device_node = device_node; + gpe_event_info->dispatch.device.node = device_node; + gpe_event_info->dispatch.device.next = NULL; + } else { + /* There are multiple devices to notify implicitly. */ + + notify_object = ACPI_ALLOCATE_ZEROED(sizeof(*notify_object)); + if (!notify_object) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + notify_object->node = device_node; + notify_object->next = gpe_event_info->dispatch.device.next; + gpe_event_info->dispatch.device.next = notify_object; } + out: gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; status = AE_OK; -- cgit v1.2.3