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