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