ACPICA: Tables: Mechanism to handle late stage acpi_get_table() imbalance
authorLv Zheng <lv.zheng@intel.com>
Wed, 7 Jun 2017 04:54:58 +0000 (12:54 +0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 12 Jun 2017 12:09:29 +0000 (14:09 +0200)
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>
drivers/acpi/acpica/tbutils.c
include/acpi/actbl.h

index 7abe6650573950674ce41a7511fd9abe2736c451..0d2e98920069cf97ee323c8f9c3df543f8d6407a 100644 (file)
@@ -416,9 +416,18 @@ acpi_tb_get_table(struct acpi_table_desc *table_desc,
                }
        }
 
-       table_desc->validation_count++;
-       if (table_desc->validation_count == 0) {
-               table_desc->validation_count--;
+       if (table_desc->validation_count < ACPI_MAX_TABLE_VALIDATIONS) {
+               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;
@@ -445,13 +454,20 @@ void acpi_tb_put_table(struct acpi_table_desc *table_desc)
 
        ACPI_FUNCTION_TRACE(acpi_tb_put_table);
 
-       if (table_desc->validation_count == 0) {
-               ACPI_WARNING((AE_INFO,
-                             "Table %p, Validation count is zero before decrement\n",
-                             table_desc));
-               return_VOID;
+       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,
+                                     "Table %p, Validation count underflows\n",
+                                     table_desc));
+                       return_VOID;
+               }
        }
-       table_desc->validation_count--;
 
        if (table_desc->validation_count == 0) {
 
index d92543f3bbfdcaaeac47252729af30ba428eb371..bdc55c0da19cd06c65e589d9071f65c2eb5c2332 100644 (file)
@@ -374,6 +374,20 @@ struct acpi_table_desc {
        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 */
 
 #define ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL  (0)        /* Virtual address, external maintained */