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