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>
This commit is contained in:
committed by
Rafael J. Wysocki
parent
186f0a0d8e
commit
83848fbe7e
@@ -416,9 +416,18 @@ acpi_tb_get_table(struct acpi_table_desc *table_desc,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (table_desc->validation_count < ACPI_MAX_TABLE_VALIDATIONS) {
|
||||||
table_desc->validation_count++;
|
table_desc->validation_count++;
|
||||||
if (table_desc->validation_count == 0) {
|
|
||||||
table_desc->validation_count--;
|
/*
|
||||||
|
* Detect validation_count overflows to ensure that the warning
|
||||||
|
* message will only be printed once.
|
||||||
|
*/
|
||||||
|
if (table_desc->validation_count >= ACPI_MAX_TABLE_VALIDATIONS) {
|
||||||
|
ACPI_WARNING((AE_INFO,
|
||||||
|
"Table %p, Validation count overflows\n",
|
||||||
|
table_desc));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_table = table_desc->pointer;
|
*out_table = table_desc->pointer;
|
||||||
@@ -445,13 +454,20 @@ void acpi_tb_put_table(struct acpi_table_desc *table_desc)
|
|||||||
|
|
||||||
ACPI_FUNCTION_TRACE(acpi_tb_put_table);
|
ACPI_FUNCTION_TRACE(acpi_tb_put_table);
|
||||||
|
|
||||||
if (table_desc->validation_count == 0) {
|
if (table_desc->validation_count < ACPI_MAX_TABLE_VALIDATIONS) {
|
||||||
|
table_desc->validation_count--;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Detect validation_count underflows to ensure that the warning
|
||||||
|
* message will only be printed once.
|
||||||
|
*/
|
||||||
|
if (table_desc->validation_count >= ACPI_MAX_TABLE_VALIDATIONS) {
|
||||||
ACPI_WARNING((AE_INFO,
|
ACPI_WARNING((AE_INFO,
|
||||||
"Table %p, Validation count is zero before decrement\n",
|
"Table %p, Validation count underflows\n",
|
||||||
table_desc));
|
table_desc));
|
||||||
return_VOID;
|
return_VOID;
|
||||||
}
|
}
|
||||||
table_desc->validation_count--;
|
}
|
||||||
|
|
||||||
if (table_desc->validation_count == 0) {
|
if (table_desc->validation_count == 0) {
|
||||||
|
|
||||||
|
|||||||
@@ -374,6 +374,20 @@ struct acpi_table_desc {
|
|||||||
u16 validation_count;
|
u16 validation_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maximum value of the validation_count field in struct acpi_table_desc.
|
||||||
|
* When reached, validation_count cannot be changed any more and the table will
|
||||||
|
* be permanently regarded as validated.
|
||||||
|
*
|
||||||
|
* This is to prevent situations in which unbalanced table get/put operations
|
||||||
|
* may cause premature table unmapping in the OS to happen.
|
||||||
|
*
|
||||||
|
* The maximum validation count can be defined to any value, but should be
|
||||||
|
* greater than the maximum number of OS early stage mapping slots to avoid
|
||||||
|
* leaking early stage table mappings to the late stage.
|
||||||
|
*/
|
||||||
|
#define ACPI_MAX_TABLE_VALIDATIONS ACPI_UINT16_MAX
|
||||||
|
|
||||||
/* Masks for Flags field above */
|
/* Masks for Flags field above */
|
||||||
|
|
||||||
#define ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL (0) /* Virtual address, external maintained */
|
#define ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL (0) /* Virtual address, external maintained */
|
||||||
|
|||||||
Reference in New Issue
Block a user