8c2ae8
From 4a0243ecb4c94e2d73510d096c5ea4d0711fc6c0 Mon Sep 17 00:00:00 2001
8c2ae8
From: Seunghun Han <kkamagui@gmail.com>
8c2ae8
Date: Fri, 23 Jun 2017 14:19:48 +0900
8c2ae8
Subject: [PATCH] acpi: acpica: fix acpi parse and parseext cache leaks
8c2ae8
MIME-Version: 1.0
8c2ae8
Content-Type: text/plain; charset=UTF-8
8c2ae8
Content-Transfer-Encoding: 8bit
8c2ae8
8c2ae8
I'm Seunghun Han, and I work for National Security Research Institute of
8c2ae8
South Korea.
8c2ae8
8c2ae8
I have been doing a research on ACPI and found an ACPI cache leak in ACPI
8c2ae8
early abort cases.
8c2ae8
8c2ae8
Boot log of ACPI cache leak is as follows:
8c2ae8
[    0.352414] ACPI: Added _OSI(Module Device)
8c2ae8
[    0.353182] ACPI: Added _OSI(Processor Device)
8c2ae8
[    0.353182] ACPI: Added _OSI(3.0 _SCP Extensions)
8c2ae8
[    0.353182] ACPI: Added _OSI(Processor Aggregator Device)
8c2ae8
[    0.356028] ACPI: Unable to start the ACPI Interpreter
8c2ae8
[    0.356799] ACPI Error: Could not remove SCI handler (20170303/evmisc-281)
8c2ae8
[    0.360215] kmem_cache_destroy Acpi-State: Slab cache still has objects
8c2ae8
[    0.360648] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G        W
8c2ae8
4.12.0-rc4-next-20170608+ #10
8c2ae8
[    0.361273] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS
8c2ae8
VirtualBox 12/01/2006
8c2ae8
[    0.361873] Call Trace:
8c2ae8
[    0.362243]  ? dump_stack+0x5c/0x81
8c2ae8
[    0.362591]  ? kmem_cache_destroy+0x1aa/0x1c0
8c2ae8
[    0.362944]  ? acpi_sleep_proc_init+0x27/0x27
8c2ae8
[    0.363296]  ? acpi_os_delete_cache+0xa/0x10
8c2ae8
[    0.363646]  ? acpi_ut_delete_caches+0x6d/0x7b
8c2ae8
[    0.364000]  ? acpi_terminate+0xa/0x14
8c2ae8
[    0.364000]  ? acpi_init+0x2af/0x34f
8c2ae8
[    0.364000]  ? __class_create+0x4c/0x80
8c2ae8
[    0.364000]  ? video_setup+0x7f/0x7f
8c2ae8
[    0.364000]  ? acpi_sleep_proc_init+0x27/0x27
8c2ae8
[    0.364000]  ? do_one_initcall+0x4e/0x1a0
8c2ae8
[    0.364000]  ? kernel_init_freeable+0x189/0x20a
8c2ae8
[    0.364000]  ? rest_init+0xc0/0xc0
8c2ae8
[    0.364000]  ? kernel_init+0xa/0x100
8c2ae8
[    0.364000]  ? ret_from_fork+0x25/0x30
8c2ae8
8c2ae8
I analyzed this memory leak in detail. I found that “Acpi-State” cache and
8c2ae8
“Acpi-Parse” cache were merged because the size of cache objects was same
8c2ae8
slab cache size.
8c2ae8
8c2ae8
I finally found “Acpi-Parse” cache and “Acpi-ParseExt” cache were leaked
8c2ae8
using SLAB_NEVER_MERGE flag in kmem_cache_create() function.
8c2ae8
8c2ae8
Real ACPI cache leak point is as follows:
8c2ae8
[    0.360101] ACPI: Added _OSI(Module Device)
8c2ae8
[    0.360101] ACPI: Added _OSI(Processor Device)
8c2ae8
[    0.360101] ACPI: Added _OSI(3.0 _SCP Extensions)
8c2ae8
[    0.361043] ACPI: Added _OSI(Processor Aggregator Device)
8c2ae8
[    0.364016] ACPI: Unable to start the ACPI Interpreter
8c2ae8
[    0.365061] ACPI Error: Could not remove SCI handler (20170303/evmisc-281)
8c2ae8
[    0.368174] kmem_cache_destroy Acpi-Parse: Slab cache still has objects
8c2ae8
[    0.369332] CPU: 1 PID: 1 Comm: swapper/0 Tainted: G        W
8c2ae8
4.12.0-rc4-next-20170608+ #8
8c2ae8
[    0.371256] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS
8c2ae8
VirtualBox 12/01/2006
8c2ae8
[    0.372000] Call Trace:
8c2ae8
[    0.372000]  ? dump_stack+0x5c/0x81
8c2ae8
[    0.372000]  ? kmem_cache_destroy+0x1aa/0x1c0
8c2ae8
[    0.372000]  ? acpi_sleep_proc_init+0x27/0x27
8c2ae8
[    0.372000]  ? acpi_os_delete_cache+0xa/0x10
8c2ae8
[    0.372000]  ? acpi_ut_delete_caches+0x56/0x7b
8c2ae8
[    0.372000]  ? acpi_terminate+0xa/0x14
8c2ae8
[    0.372000]  ? acpi_init+0x2af/0x34f
8c2ae8
[    0.372000]  ? __class_create+0x4c/0x80
8c2ae8
[    0.372000]  ? video_setup+0x7f/0x7f
8c2ae8
[    0.372000]  ? acpi_sleep_proc_init+0x27/0x27
8c2ae8
[    0.372000]  ? do_one_initcall+0x4e/0x1a0
8c2ae8
[    0.372000]  ? kernel_init_freeable+0x189/0x20a
8c2ae8
[    0.372000]  ? rest_init+0xc0/0xc0
8c2ae8
[    0.372000]  ? kernel_init+0xa/0x100
8c2ae8
[    0.372000]  ? ret_from_fork+0x25/0x30
8c2ae8
[    0.388039] kmem_cache_destroy Acpi-ParseExt: Slab cache still has objects
8c2ae8
[    0.389063] CPU: 1 PID: 1 Comm: swapper/0 Tainted: G        W
8c2ae8
4.12.0-rc4-next-20170608+ #8
8c2ae8
[    0.390557] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS
8c2ae8
VirtualBox 12/01/2006
8c2ae8
[    0.392000] Call Trace:
8c2ae8
[    0.392000]  ? dump_stack+0x5c/0x81
8c2ae8
[    0.392000]  ? kmem_cache_destroy+0x1aa/0x1c0
8c2ae8
[    0.392000]  ? acpi_sleep_proc_init+0x27/0x27
8c2ae8
[    0.392000]  ? acpi_os_delete_cache+0xa/0x10
8c2ae8
[    0.392000]  ? acpi_ut_delete_caches+0x6d/0x7b
8c2ae8
[    0.392000]  ? acpi_terminate+0xa/0x14
8c2ae8
[    0.392000]  ? acpi_init+0x2af/0x34f
8c2ae8
[    0.392000]  ? __class_create+0x4c/0x80
8c2ae8
[    0.392000]  ? video_setup+0x7f/0x7f
8c2ae8
[    0.392000]  ? acpi_sleep_proc_init+0x27/0x27
8c2ae8
[    0.392000]  ? do_one_initcall+0x4e/0x1a0
8c2ae8
[    0.392000]  ? kernel_init_freeable+0x189/0x20a
8c2ae8
[    0.392000]  ? rest_init+0xc0/0xc0
8c2ae8
[    0.392000]  ? kernel_init+0xa/0x100
8c2ae8
[    0.392000]  ? ret_from_fork+0x25/0x30
8c2ae8
8c2ae8
When early abort is occurred due to invalid ACPI information, Linux kernel
8c2ae8
terminates ACPI by calling acpi_terminate() function. The function calls
8c2ae8
acpi_ut_delete_caches() function to delete local caches (acpi_gbl_namespace_
8c2ae8
cache, state_cache, operand_cache, ps_node_cache, ps_node_ext_cache).
8c2ae8
8c2ae8
But the deletion codes in acpi_ut_delete_caches() function only delete
8c2ae8
slab caches using kmem_cache_destroy() function, therefore the cache
8c2ae8
objects should be flushed before acpi_ut_delete_caches() function.
8c2ae8
8c2ae8
“Acpi-Parse” cache and “Acpi-ParseExt” cache are used in an AML parse
8c2ae8
function, acpi_ps_parse_loop(). The function should have flush codes to
8c2ae8
handle an error state due to invalid AML codes.
8c2ae8
8c2ae8
This cache leak has a security threat because an old kernel (<= 4.9) shows
8c2ae8
memory locations of kernel functions in stack dump. Some malicious users
8c2ae8
could use this information to neutralize kernel ASLR.
8c2ae8
8c2ae8
To fix ACPI cache leak for enhancing security, I made a patch which has
8c2ae8
flush codes in acpi_ps_parse_loop() function.
8c2ae8
8c2ae8
I hope that this patch improves the security of Linux kernel.
8c2ae8
8c2ae8
Thank you.
8c2ae8
8c2ae8
Signed-off-by: Seunghun Han <kkamagui@gmail.com>
8c2ae8
8c2ae8
Github-Location: https://github.com/acpica/acpica/pull/278/commits/4a0243ecb4c94e2d73510d096c5ea4d0711fc6c0
8c2ae8
8c2ae8
---
8c2ae8
 source/components/parser/psobject.c | 44 ++++++++++++++-----------------------
8c2ae8
 1 file changed, 16 insertions(+), 28 deletions(-)
8c2ae8
8c2ae8
Index: acpica-unix2-20180531/source/components/parser/psobject.c
8c2ae8
===================================================================
8c2ae8
--- acpica-unix2-20180531.orig/source/components/parser/psobject.c
8c2ae8
+++ acpica-unix2-20180531/source/components/parser/psobject.c
8c2ae8
@@ -709,7 +709,8 @@ AcpiPsCompleteFinalOp (
8c2ae8
     ACPI_PARSE_OBJECT       *Op,
8c2ae8
     ACPI_STATUS             Status)
8c2ae8
 {
8c2ae8
-    ACPI_STATUS             Status2;
8c2ae8
+    ACPI_STATUS             ReturnStatus = AE_OK;
8c2ae8
+    BOOLEAN                 Ascending = TRUE;
8c2ae8
 
8c2ae8
 
8c2ae8
     ACPI_FUNCTION_TRACE_PTR (PsCompleteFinalOp, WalkState);
8c2ae8
@@ -726,7 +727,7 @@ AcpiPsCompleteFinalOp (
8c2ae8
     {
8c2ae8
         if (Op)
8c2ae8
         {
8c2ae8
-            if (WalkState->AscendingCallback != NULL)
8c2ae8
+            if (Ascending && WalkState->AscendingCallback != NULL)
8c2ae8
             {
8c2ae8
                 WalkState->Op = Op;
8c2ae8
                 WalkState->OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
8c2ae8
@@ -745,41 +746,28 @@ AcpiPsCompleteFinalOp (
8c2ae8
 
8c2ae8
                 if (Status == AE_CTRL_TERMINATE)
8c2ae8
                 {
8c2ae8
-                    Status = AE_OK;
8c2ae8
-
8c2ae8
-                    /* Clean up */
8c2ae8
-                    do
8c2ae8
-                    {
8c2ae8
-                        if (Op)
8c2ae8
-                        {
8c2ae8
-                            Status2 = AcpiPsCompleteThisOp (WalkState, Op);
8c2ae8
-                            if (ACPI_FAILURE (Status2))
8c2ae8
-                            {
8c2ae8
-                                return_ACPI_STATUS (Status2);
8c2ae8
-                            }
8c2ae8
-                        }
8c2ae8
-
8c2ae8
-                        AcpiPsPopScope (&(WalkState->ParserState), &Op,
8c2ae8
-                            &WalkState->ArgTypes, &WalkState->ArgCount);
8c2ae8
-
8c2ae8
-                    } while (Op);
8c2ae8
-
8c2ae8
-                    return_ACPI_STATUS (Status);
8c2ae8
+                    Ascending = FALSE;
8c2ae8
+                    ReturnStatus = AE_CTRL_TERMINATE;
8c2ae8
                 }
8c2ae8
 
8c2ae8
                 else if (ACPI_FAILURE (Status))
8c2ae8
                 {
8c2ae8
                     /* First error is most important */
8c2ae8
 
8c2ae8
-                    (void) AcpiPsCompleteThisOp (WalkState, Op);
8c2ae8
-                    return_ACPI_STATUS (Status);
8c2ae8
+                    Ascending = FALSE;
8c2ae8
+                    ReturnStatus = Status;
8c2ae8
                 }
8c2ae8
             }
8c2ae8
 
8c2ae8
-            Status2 = AcpiPsCompleteThisOp (WalkState, Op);
8c2ae8
-            if (ACPI_FAILURE (Status2))
8c2ae8
+            Status = AcpiPsCompleteThisOp (WalkState, Op);
8c2ae8
+            if (ACPI_FAILURE (Status))
8c2ae8
             {
8c2ae8
-                return_ACPI_STATUS (Status2);
8c2ae8
+                Ascending = FALSE;
8c2ae8
+                if (ACPI_SUCCESS (ReturnStatus) ||
8c2ae8
+                    ReturnStatus == AE_CTRL_TERMINATE)
8c2ae8
+                {
8c2ae8
+                    ReturnStatus = Status;
8c2ae8
+                }
8c2ae8
             }
8c2ae8
         }
8c2ae8
 
8c2ae8
@@ -788,5 +776,5 @@ AcpiPsCompleteFinalOp (
8c2ae8
 
8c2ae8
     } while (Op);
8c2ae8
 
8c2ae8
-    return_ACPI_STATUS (Status);
8c2ae8
+    return_ACPI_STATUS (ReturnStatus);
8c2ae8
 }