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