diff options
author | Lv Zheng <lv.zheng@intel.com> | 2017-06-07 00:54:58 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2017-06-12 08:09:29 -0400 |
commit | 83848fbe7e6af978c080a88c130a67178b1ac0e4 (patch) | |
tree | d0768387438df3a229e80017c093e826f0b30cec | |
parent | 186f0a0d8e083505bd5cd23baa82b2205224d9ad (diff) |
ACPICA: Tables: Mechanism to handle late stage acpi_get_table() imbalance
Considering this case:
1. A program opens a sysfs table file 65535 times, it can increase
validation_count and first increment cause the table to be mapped:
validation_count = 65535
2. AML execution causes "Load" to be executed on the same
table, this time it cannot increase validation_count, so
validation_count remains:
validation_count = 65535
3. The program closes sysfs table file 65535 times, it can decrease
validation_count and the last decrement cause the table to be
unmapped:
validation_count = 0
4. AML code still accessing the loaded table, kernel crash can be
observed.
To prevent that from happening, add a validation_count threashold.
When it is reached, the validation_count can no longer be
incremented/decremented to invalidate the table descriptor (means
preventing table unmappings)
Note that code added in acpi_tb_put_table() is actually a no-op but
changes the warning message into a "warn once" one. Lv Zheng.
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
[ rjw: Changelog, comments ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/acpi/acpica/tbutils.c | 34 | ||||
-rw-r--r-- | include/acpi/actbl.h | 14 |
2 files changed, 39 insertions, 9 deletions
diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index 7abe66505739..0d2e98920069 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c | |||
@@ -416,9 +416,18 @@ acpi_tb_get_table(struct acpi_table_desc *table_desc, | |||
416 | } | 416 | } |
417 | } | 417 | } |
418 | 418 | ||
419 | table_desc->validation_count++; | 419 | if (table_desc->validation_count < ACPI_MAX_TABLE_VALIDATIONS) { |
420 | if (table_desc->validation_count == 0) { | 420 | table_desc->validation_count++; |
421 | table_desc->validation_count--; | 421 | |
422 | /* | ||
423 | * Detect validation_count overflows to ensure that the warning | ||
424 | * message will only be printed once. | ||
425 | */ | ||
426 | if (table_desc->validation_count >= ACPI_MAX_TABLE_VALIDATIONS) { | ||
427 | ACPI_WARNING((AE_INFO, | ||
428 | "Table %p, Validation count overflows\n", | ||
429 | table_desc)); | ||
430 | } | ||
422 | } | 431 | } |
423 | 432 | ||
424 | *out_table = table_desc->pointer; | 433 | *out_table = table_desc->pointer; |
@@ -445,13 +454,20 @@ void acpi_tb_put_table(struct acpi_table_desc *table_desc) | |||
445 | 454 | ||
446 | ACPI_FUNCTION_TRACE(acpi_tb_put_table); | 455 | ACPI_FUNCTION_TRACE(acpi_tb_put_table); |
447 | 456 | ||
448 | if (table_desc->validation_count == 0) { | 457 | if (table_desc->validation_count < ACPI_MAX_TABLE_VALIDATIONS) { |
449 | ACPI_WARNING((AE_INFO, | 458 | table_desc->validation_count--; |
450 | "Table %p, Validation count is zero before decrement\n", | 459 | |
451 | table_desc)); | 460 | /* |
452 | return_VOID; | 461 | * Detect validation_count underflows to ensure that the warning |
462 | * message will only be printed once. | ||
463 | */ | ||
464 | if (table_desc->validation_count >= ACPI_MAX_TABLE_VALIDATIONS) { | ||
465 | ACPI_WARNING((AE_INFO, | ||
466 | "Table %p, Validation count underflows\n", | ||
467 | table_desc)); | ||
468 | return_VOID; | ||
469 | } | ||
453 | } | 470 | } |
454 | table_desc->validation_count--; | ||
455 | 471 | ||
456 | if (table_desc->validation_count == 0) { | 472 | if (table_desc->validation_count == 0) { |
457 | 473 | ||
diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index d92543f3bbfd..bdc55c0da19c 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h | |||
@@ -374,6 +374,20 @@ struct acpi_table_desc { | |||
374 | u16 validation_count; | 374 | u16 validation_count; |
375 | }; | 375 | }; |
376 | 376 | ||
377 | /* | ||
378 | * Maximum value of the validation_count field in struct acpi_table_desc. | ||
379 | * When reached, validation_count cannot be changed any more and the table will | ||
380 | * be permanently regarded as validated. | ||
381 | * | ||
382 | * This is to prevent situations in which unbalanced table get/put operations | ||
383 | * may cause premature table unmapping in the OS to happen. | ||
384 | * | ||
385 | * The maximum validation count can be defined to any value, but should be | ||
386 | * greater than the maximum number of OS early stage mapping slots to avoid | ||
387 | * leaking early stage table mappings to the late stage. | ||
388 | */ | ||
389 | #define ACPI_MAX_TABLE_VALIDATIONS ACPI_UINT16_MAX | ||
390 | |||
377 | /* Masks for Flags field above */ | 391 | /* Masks for Flags field above */ |
378 | 392 | ||
379 | #define ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL (0) /* Virtual address, external maintained */ | 393 | #define ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL (0) /* Virtual address, external maintained */ |