]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.11-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 9 Mar 2021 13:46:31 +0000 (14:46 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 9 Mar 2021 13:46:31 +0000 (14:46 +0100)
added patches:
acpica-fix-race-in-generic_serial_bus-i2c-and-gpio-op_region-parameter-handling.patch

queue-5.11/acpica-fix-race-in-generic_serial_bus-i2c-and-gpio-op_region-parameter-handling.patch [new file with mode: 0644]
queue-5.11/series [new file with mode: 0644]

diff --git a/queue-5.11/acpica-fix-race-in-generic_serial_bus-i2c-and-gpio-op_region-parameter-handling.patch b/queue-5.11/acpica-fix-race-in-generic_serial_bus-i2c-and-gpio-op_region-parameter-handling.patch
new file mode 100644 (file)
index 0000000..e7e334e
--- /dev/null
@@ -0,0 +1,237 @@
+From c27f3d011b08540e68233cf56274fdc34bebb9b5 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Thu, 18 Feb 2021 15:17:07 -0800
+Subject: ACPICA: Fix race in generic_serial_bus (I2C) and GPIO op_region parameter handling
+
+From: Hans de Goede <hdegoede@redhat.com>
+
+commit c27f3d011b08540e68233cf56274fdc34bebb9b5 upstream.
+
+ACPICA commit c9e0116952363b0fa815143dca7e9a2eb4fefa61
+
+The handling of the generic_serial_bus (I2C) and GPIO op_regions in
+acpi_ev_address_space_dispatch() passes a number of extra parameters
+to the address-space handler through the address-space Context pointer
+(instead of using more function parameters).
+
+The Context is shared between threads, so if multiple threads try to
+call the handler for the same address-space at the same time, then
+a second thread could change the parameters of a first thread while
+the handler is running for the first thread.
+
+An example of this race hitting is the Lenovo Yoga Tablet2 1015L,
+where there are both attrib_bytes accesses and attrib_byte accesses
+to the same address-space. The attrib_bytes access stores the number
+of bytes to transfer in Context->access_length. Where as for the
+attrib_byte access the number of bytes to transfer is always 1 and
+field_obj->Field.access_length is unused (so 0). Both types of
+accesses racing from different threads leads to the following problem:
+
+ 1. Thread a. starts an attrib_bytes access, stores a non 0 value
+    from field_obj->Field.access_length in Context->access_length
+
+ 2. Thread b. starts an attrib_byte access, stores 0 in
+    Context->access_length
+
+ 3. Thread a. calls i2c_acpi_space_handler() (under Linux). Which
+    sees that the access-type is ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE
+    and calls acpi_gsb_i2c_read_bytes(..., Context->access_length)
+
+ 4. At this point Context->access_length is 0 (set by thread b.)
+
+rather then the field_obj->Field.access_length value from thread a.
+This 0 length reads leads to the following errors being logged:
+
+ i2c i2c-0: adapter quirk: no zero length (addr 0x0078, size 0, read)
+ i2c i2c-0: i2c read 0 bytes from client@0x78 starting at reg 0x0 failed, error: -95
+
+Note this is just an example of the problems which this race can cause.
+
+There are likely many more (sporadic) problems caused by this race.
+
+This commit adds a new context_mutex to struct acpi_object_addr_handler
+and makes acpi_ev_address_space_dispatch() take that mutex when
+using the shared Context to pass extra parameters to an address-space
+handler, fixing this race.
+
+Note the new mutex must be taken *after* exiting the interpreter,
+therefor the existing acpi_ex_exit_interpreter() call is moved to above
+the code which stores the extra parameters in the Context.
+
+Link: https://github.com/acpica/acpica/commit/c9e01169
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Bob Moore <robert.moore@intel.com>
+Signed-off-by: Erik Kaneda <erik.kaneda@intel.com>
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/acpica/acobject.h  |    1 
+ drivers/acpi/acpica/evhandler.c |    7 ++++
+ drivers/acpi/acpica/evregion.c  |   64 +++++++++++++++++++++++++++++-----------
+ drivers/acpi/acpica/evxfregn.c  |    2 +
+ 4 files changed, 57 insertions(+), 17 deletions(-)
+
+--- a/drivers/acpi/acpica/acobject.h
++++ b/drivers/acpi/acpica/acobject.h
+@@ -284,6 +284,7 @@ struct acpi_object_addr_handler {
+       acpi_adr_space_handler handler;
+       struct acpi_namespace_node *node;       /* Parent device */
+       void *context;
++      acpi_mutex context_mutex;
+       acpi_adr_space_setup setup;
+       union acpi_operand_object *region_list; /* Regions using this handler */
+       union acpi_operand_object *next;
+--- a/drivers/acpi/acpica/evhandler.c
++++ b/drivers/acpi/acpica/evhandler.c
+@@ -489,6 +489,13 @@ acpi_ev_install_space_handler(struct acp
+       /* Init handler obj */
++      status =
++          acpi_os_create_mutex(&handler_obj->address_space.context_mutex);
++      if (ACPI_FAILURE(status)) {
++              acpi_ut_remove_reference(handler_obj);
++              goto unlock_and_exit;
++      }
++
+       handler_obj->address_space.space_id = (u8)space_id;
+       handler_obj->address_space.handler_flags = flags;
+       handler_obj->address_space.region_list = NULL;
+--- a/drivers/acpi/acpica/evregion.c
++++ b/drivers/acpi/acpica/evregion.c
+@@ -112,6 +112,8 @@ acpi_ev_address_space_dispatch(union acp
+       union acpi_operand_object *region_obj2;
+       void *region_context = NULL;
+       struct acpi_connection_info *context;
++      acpi_mutex context_mutex;
++      u8 context_locked;
+       acpi_physical_address address;
+       ACPI_FUNCTION_TRACE(ev_address_space_dispatch);
+@@ -136,6 +138,8 @@ acpi_ev_address_space_dispatch(union acp
+       }
+       context = handler_desc->address_space.context;
++      context_mutex = handler_desc->address_space.context_mutex;
++      context_locked = FALSE;
+       /*
+        * It may be the case that the region has never been initialized.
+@@ -204,6 +208,23 @@ acpi_ev_address_space_dispatch(union acp
+       handler = handler_desc->address_space.handler;
+       address = (region_obj->region.address + region_offset);
++      ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
++                        "Handler %p (@%p) Address %8.8X%8.8X [%s]\n",
++                        &region_obj->region.handler->address_space, handler,
++                        ACPI_FORMAT_UINT64(address),
++                        acpi_ut_get_region_name(region_obj->region.
++                                                space_id)));
++
++      if (!(handler_desc->address_space.handler_flags &
++            ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) {
++              /*
++               * For handlers other than the default (supplied) handlers, we must
++               * exit the interpreter because the handler *might* block -- we don't
++               * know what it will do, so we can't hold the lock on the interpreter.
++               */
++              acpi_ex_exit_interpreter();
++      }
++
+       /*
+        * Special handling for generic_serial_bus and general_purpose_io:
+        * There are three extra parameters that must be passed to the
+@@ -212,6 +233,11 @@ acpi_ev_address_space_dispatch(union acp
+        *   2) Length of the above buffer
+        *   3) Actual access length from the access_as() op
+        *
++       * Since we pass these extra parameters via the context, which is
++       * shared between threads, we must lock the context to avoid these
++       * parameters being changed from another thread before the handler
++       * has completed running.
++       *
+        * In addition, for general_purpose_io, the Address and bit_width fields
+        * are defined as follows:
+        *   1) Address is the pin number index of the field (bit offset from
+@@ -221,6 +247,14 @@ acpi_ev_address_space_dispatch(union acp
+       if ((region_obj->region.space_id == ACPI_ADR_SPACE_GSBUS) &&
+           context && field_obj) {
++              status =
++                  acpi_os_acquire_mutex(context_mutex, ACPI_WAIT_FOREVER);
++              if (ACPI_FAILURE(status)) {
++                      goto re_enter_interpreter;
++              }
++
++              context_locked = TRUE;
++
+               /* Get the Connection (resource_template) buffer */
+               context->connection = field_obj->field.resource_buffer;
+@@ -230,6 +264,14 @@ acpi_ev_address_space_dispatch(union acp
+       if ((region_obj->region.space_id == ACPI_ADR_SPACE_GPIO) &&
+           context && field_obj) {
++              status =
++                  acpi_os_acquire_mutex(context_mutex, ACPI_WAIT_FOREVER);
++              if (ACPI_FAILURE(status)) {
++                      goto re_enter_interpreter;
++              }
++
++              context_locked = TRUE;
++
+               /* Get the Connection (resource_template) buffer */
+               context->connection = field_obj->field.resource_buffer;
+@@ -239,28 +281,15 @@ acpi_ev_address_space_dispatch(union acp
+               bit_width = field_obj->field.bit_length;
+       }
+-      ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+-                        "Handler %p (@%p) Address %8.8X%8.8X [%s]\n",
+-                        &region_obj->region.handler->address_space, handler,
+-                        ACPI_FORMAT_UINT64(address),
+-                        acpi_ut_get_region_name(region_obj->region.
+-                                                space_id)));
+-
+-      if (!(handler_desc->address_space.handler_flags &
+-            ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) {
+-              /*
+-               * For handlers other than the default (supplied) handlers, we must
+-               * exit the interpreter because the handler *might* block -- we don't
+-               * know what it will do, so we can't hold the lock on the interpreter.
+-               */
+-              acpi_ex_exit_interpreter();
+-      }
+-
+       /* Call the handler */
+       status = handler(function, address, bit_width, value, context,
+                        region_obj2->extra.region_context);
++      if (context_locked) {
++              acpi_os_release_mutex(context_mutex);
++      }
++
+       if (ACPI_FAILURE(status)) {
+               ACPI_EXCEPTION((AE_INFO, status, "Returned by Handler for [%s]",
+                               acpi_ut_get_region_name(region_obj->region.
+@@ -277,6 +306,7 @@ acpi_ev_address_space_dispatch(union acp
+               }
+       }
++re_enter_interpreter:
+       if (!(handler_desc->address_space.handler_flags &
+             ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) {
+               /*
+--- a/drivers/acpi/acpica/evxfregn.c
++++ b/drivers/acpi/acpica/evxfregn.c
+@@ -201,6 +201,8 @@ acpi_remove_address_space_handler(acpi_h
+                       /* Now we can delete the handler object */
++                      acpi_os_release_mutex(handler_obj->address_space.
++                                            context_mutex);
+                       acpi_ut_remove_reference(handler_obj);
+                       goto unlock_and_exit;
+               }
diff --git a/queue-5.11/series b/queue-5.11/series
new file mode 100644 (file)
index 0000000..c73e973
--- /dev/null
@@ -0,0 +1 @@
+acpica-fix-race-in-generic_serial_bus-i2c-and-gpio-op_region-parameter-handling.patch