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