CVE-2017-11472: Improper Handling of Exceptional Conditions (CWE-755)
value: The acpi_ns_terminate() function in drivers/acpi/acpica/nsutils.c in the Linux kernel before 4.12 does not flush the operand cache and causes a kernel stack dump, which allows local users to obtain sensitive information from kernel memory and bypass the KASLR protection mechanism (in the kernel through 4.9) via a crafted ACPI table.
CWE description: The product does not handle or incorrectly handles an exceptional condition.
CVSS v3 base score: 7.1
Published: 2017-07-20T04:29Z
Repository: https://github.com/acpica/acpica
Language: C
Files changed: source/components/namespace/nsutils.c
Commit date: 2017-02-22 16:32:49 +0800
Commit message: Namespace: fix operand cache leak

I found some ACPI operand cache leaks in ACPI early abort cases.

Boot log of ACPI operand cache leak is as follows:
>[    0.174332] ACPI: Added _OSI(Module Device)
>[    0.175504] ACPI: Added _OSI(Processor Device)
>[    0.176010] ACPI: Added _OSI(3.0 _SCP Extensions)
>[    0.177032] ACPI: Added _OSI(Processor Aggregator Device)
>[    0.178284] ACPI: SCI (IRQ16705) allocation failed
>[    0.179352] ACPI Exception: AE_NOT_ACQUIRED, Unable to install
System Control Interrupt handler (20160930/evevent-131)
>[    0.180008] ACPI: Unable to start the ACPI Interpreter
>[    0.181125] ACPI Error: Could not remove SCI handler
(20160930/evmisc-281)
>[    0.184068] kmem_cache_destroy Acpi-Operand: Slab cache still has
objects
>[    0.185358] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.10.0-rc3 #2
>[    0.186820] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS
VirtualBox 12/01/2006
>[    0.188000] Call Trace:
>[    0.188000]  ? dump_stack+0x5c/0x7d
>[    0.188000]  ? kmem_cache_destroy+0x224/0x230
>[    0.188000]  ? acpi_sleep_proc_init+0x22/0x22
>[    0.188000]  ? acpi_os_delete_cache+0xa/0xd
>[    0.188000]  ? acpi_ut_delete_caches+0x3f/0x7b
>[    0.188000]  ? acpi_terminate+0x5/0xf
>[    0.188000]  ? acpi_init+0x288/0x32e
>[    0.188000]  ? __class_create+0x4c/0x80
>[    0.188000]  ? video_setup+0x7a/0x7a
>[    0.188000]  ? do_one_initcall+0x4e/0x1b0
>[    0.188000]  ? kernel_init_freeable+0x194/0x21a
>[    0.188000]  ? rest_init+0x80/0x80
>[    0.188000]  ? kernel_init+0xa/0x100
>[    0.188000]  ? ret_from_fork+0x25/0x30

When early abort is occurred due to invalid ACPI information, Linux kernel
terminates ACPI by calling AcpiTerminate() function. The function calls
AcpiNsTerminate() function to delete namespace data and ACPI operand cache
(AcpiGbl_ModuleCodeList).

But the deletion code in AcpiNsTerminate() function is wrapped in
ACPI_EXEC_APP definition, therefore the code is only executed when the
definition exists. If the define doesn't exist, ACPI operand cache
(AcpiGbl_ModuleCodeList) is leaked, and stack dump is shown in kernel log.

This causes a security threat because the old kernel (<= 4.9) shows memory
locations of kernel functions in stack dump, therefore kernel ASLR can be
neutralized.

To fix ACPI operand leak for enhancing security, I made a patch which
removes the ACPI_EXEC_APP define in AcpiNsTerminate() function for
executing the deletion code unconditionally.

Signed-off-by: Seunghun Han <kkamagui@gmail.com>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>

Diff: diff --git a/source/components/namespace/nsutils.c b/source/components/namespace/nsutils.c
index 601bc2e04..b2917e557 100644
--- a/source/components/namespace/nsutils.c
+++ b/source/components/namespace/nsutils.c
@@ -764,32 +764,27 @@ AcpiNsValidateHandle (
 void
 AcpiNsTerminate (
     void)
 {
     ACPI_STATUS             Status;
+    ACPI_OPERAND_OBJECT     *Prev;
+    ACPI_OPERAND_OBJECT     *Next;
 
 
     ACPI_FUNCTION_TRACE (NsTerminate);
 
 
-#ifdef ACPI_EXEC_APP
-    {
-        ACPI_OPERAND_OBJECT     *Prev;
-        ACPI_OPERAND_OBJECT     *Next;
-
-        /* Delete any module-level code blocks */
+    /* Delete any module-level code blocks */
 
-        Next = AcpiGbl_ModuleCodeList;
-        while (Next)
-        {
-            Prev = Next;
-            Next = Next->Method.Mutex;
-            Prev->Method.Mutex = NULL; /* Clear the Mutex (cheated) field */
-            AcpiUtRemoveReference (Prev);
-        }
+    Next = AcpiGbl_ModuleCodeList;
+    while (Next)
+    {
+        Prev = Next;
+        Next = Next->Method.Mutex;
+        Prev->Method.Mutex = NULL; /* Clear the Mutex (cheated) field */
+        AcpiUtRemoveReference (Prev);
     }
-#endif
 
     /*
      * Free the entire namespace -- all nodes and all objects
      * attached to the nodes
      */

Vulnerable code: #ifdef ACPI_EXEC_APP
    {
        ACPI_OPERAND_OBJECT     *Prev;
        ACPI_OPERAND_OBJECT     *Next;

        /* Delete any module-level code blocks */
        Next = AcpiGbl_ModuleCodeList;
        while (Next)
        {
            Prev = Next;
            Next = Next->Method.Mutex;
            Prev->Method.Mutex = NULL; /* Clear the Mutex (cheated) field */
            AcpiUtRemoveReference (Prev);
        }
#endif
Fixed code:     ACPI_OPERAND_OBJECT     *Prev;
    ACPI_OPERAND_OBJECT     *Next;
    /* Delete any module-level code blocks */
    Next = AcpiGbl_ModuleCodeList;
    while (Next)
    {
        Prev = Next;
        Next = Next->Method.Mutex;
        Prev->Method.Mutex = NULL; /* Clear the Mutex (cheated) field */
        AcpiUtRemoveReference (Prev);