Skip to content

Commit c27f3d0

Browse files
jwrdegoederafaeljw
authored andcommitted
ACPICA: Fix race in generic_serial_bus (I2C) and GPIO op_region parameter handling
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: acpica/acpica@c9e01169 Signed-off-by: Hans de Goede <[email protected]> Signed-off-by: Bob Moore <[email protected]> Signed-off-by: Erik Kaneda <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 938bdd1 commit c27f3d0

4 files changed

Lines changed: 57 additions & 17 deletions

File tree

drivers/acpi/acpica/acobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ struct acpi_object_addr_handler {
284284
acpi_adr_space_handler handler;
285285
struct acpi_namespace_node *node; /* Parent device */
286286
void *context;
287+
acpi_mutex context_mutex;
287288
acpi_adr_space_setup setup;
288289
union acpi_operand_object *region_list; /* Regions using this handler */
289290
union acpi_operand_object *next;

drivers/acpi/acpica/evhandler.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,13 @@ acpi_ev_install_space_handler(struct acpi_namespace_node *node,
489489

490490
/* Init handler obj */
491491

492+
status =
493+
acpi_os_create_mutex(&handler_obj->address_space.context_mutex);
494+
if (ACPI_FAILURE(status)) {
495+
acpi_ut_remove_reference(handler_obj);
496+
goto unlock_and_exit;
497+
}
498+
492499
handler_obj->address_space.space_id = (u8)space_id;
493500
handler_obj->address_space.handler_flags = flags;
494501
handler_obj->address_space.region_list = NULL;

drivers/acpi/acpica/evregion.c

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
112112
union acpi_operand_object *region_obj2;
113113
void *region_context = NULL;
114114
struct acpi_connection_info *context;
115+
acpi_mutex context_mutex;
116+
u8 context_locked;
115117
acpi_physical_address address;
116118

117119
ACPI_FUNCTION_TRACE(ev_address_space_dispatch);
@@ -136,6 +138,8 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
136138
}
137139

138140
context = handler_desc->address_space.context;
141+
context_mutex = handler_desc->address_space.context_mutex;
142+
context_locked = FALSE;
139143

140144
/*
141145
* It may be the case that the region has never been initialized.
@@ -204,6 +208,23 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
204208
handler = handler_desc->address_space.handler;
205209
address = (region_obj->region.address + region_offset);
206210

211+
ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
212+
"Handler %p (@%p) Address %8.8X%8.8X [%s]\n",
213+
&region_obj->region.handler->address_space, handler,
214+
ACPI_FORMAT_UINT64(address),
215+
acpi_ut_get_region_name(region_obj->region.
216+
space_id)));
217+
218+
if (!(handler_desc->address_space.handler_flags &
219+
ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) {
220+
/*
221+
* For handlers other than the default (supplied) handlers, we must
222+
* exit the interpreter because the handler *might* block -- we don't
223+
* know what it will do, so we can't hold the lock on the interpreter.
224+
*/
225+
acpi_ex_exit_interpreter();
226+
}
227+
207228
/*
208229
* Special handling for generic_serial_bus and general_purpose_io:
209230
* There are three extra parameters that must be passed to the
@@ -212,6 +233,11 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
212233
* 2) Length of the above buffer
213234
* 3) Actual access length from the access_as() op
214235
*
236+
* Since we pass these extra parameters via the context, which is
237+
* shared between threads, we must lock the context to avoid these
238+
* parameters being changed from another thread before the handler
239+
* has completed running.
240+
*
215241
* In addition, for general_purpose_io, the Address and bit_width fields
216242
* are defined as follows:
217243
* 1) Address is the pin number index of the field (bit offset from
@@ -221,6 +247,14 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
221247
if ((region_obj->region.space_id == ACPI_ADR_SPACE_GSBUS) &&
222248
context && field_obj) {
223249

250+
status =
251+
acpi_os_acquire_mutex(context_mutex, ACPI_WAIT_FOREVER);
252+
if (ACPI_FAILURE(status)) {
253+
goto re_enter_interpreter;
254+
}
255+
256+
context_locked = TRUE;
257+
224258
/* Get the Connection (resource_template) buffer */
225259

226260
context->connection = field_obj->field.resource_buffer;
@@ -230,6 +264,14 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
230264
if ((region_obj->region.space_id == ACPI_ADR_SPACE_GPIO) &&
231265
context && field_obj) {
232266

267+
status =
268+
acpi_os_acquire_mutex(context_mutex, ACPI_WAIT_FOREVER);
269+
if (ACPI_FAILURE(status)) {
270+
goto re_enter_interpreter;
271+
}
272+
273+
context_locked = TRUE;
274+
233275
/* Get the Connection (resource_template) buffer */
234276

235277
context->connection = field_obj->field.resource_buffer;
@@ -239,28 +281,15 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
239281
bit_width = field_obj->field.bit_length;
240282
}
241283

242-
ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
243-
"Handler %p (@%p) Address %8.8X%8.8X [%s]\n",
244-
&region_obj->region.handler->address_space, handler,
245-
ACPI_FORMAT_UINT64(address),
246-
acpi_ut_get_region_name(region_obj->region.
247-
space_id)));
248-
249-
if (!(handler_desc->address_space.handler_flags &
250-
ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) {
251-
/*
252-
* For handlers other than the default (supplied) handlers, we must
253-
* exit the interpreter because the handler *might* block -- we don't
254-
* know what it will do, so we can't hold the lock on the interpreter.
255-
*/
256-
acpi_ex_exit_interpreter();
257-
}
258-
259284
/* Call the handler */
260285

261286
status = handler(function, address, bit_width, value, context,
262287
region_obj2->extra.region_context);
263288

289+
if (context_locked) {
290+
acpi_os_release_mutex(context_mutex);
291+
}
292+
264293
if (ACPI_FAILURE(status)) {
265294
ACPI_EXCEPTION((AE_INFO, status, "Returned by Handler for [%s]",
266295
acpi_ut_get_region_name(region_obj->region.
@@ -277,6 +306,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
277306
}
278307
}
279308

309+
re_enter_interpreter:
280310
if (!(handler_desc->address_space.handler_flags &
281311
ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) {
282312
/*

drivers/acpi/acpica/evxfregn.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ acpi_remove_address_space_handler(acpi_handle device,
201201

202202
/* Now we can delete the handler object */
203203

204+
acpi_os_release_mutex(handler_obj->address_space.
205+
context_mutex);
204206
acpi_ut_remove_reference(handler_obj);
205207
goto unlock_and_exit;
206208
}

0 commit comments

Comments
 (0)