debug: add more strict checking for show_regs [2/2]
authorJiamin Ma <jiamin.ma@amlogic.com>
Tue, 30 Apr 2019 08:07:17 +0000 (16:07 +0800)
committerJianxin Pan <jianxin.pan@amlogic.com>
Tue, 18 Jun 2019 05:36:36 +0000 (22:36 -0700)
PD#SWPL-7711

Problem:
Executing echo l > /proc/sysrq-trigger each 5 seconds for about
15 minius will trigger hardlockup

Solution:
Add more strict checking for show_regs to filter out addresses
in secure monitor region and ioremap region, deferencing which
triggers external abort on none-linefetch, and finally leading
to hardlockup

Verify:
Locally pass on U200

Change-Id: I6bd219e7dc3ad29904e6bd1b7d2f4cfb3928d8ed
Signed-off-by: Jiamin Ma <jiamin.ma@amlogic.com>
arch/arm/kernel/process.c
arch/arm64/kernel/process.c
drivers/amlogic/secmon/secmon.c
drivers/tty/sysrq.c
include/linux/amlogic/secmon.h
lib/nmi_backtrace.c
mm/vmalloc.c

index c0c1730..7468ede 100644 (file)
@@ -47,6 +47,10 @@ unsigned long __stack_chk_guard __read_mostly;
 EXPORT_SYMBOL(__stack_chk_guard);
 #endif
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+#include <linux/amlogic/secmon.h>
+#endif
+
 static const char *processor_modes[] __maybe_unused = {
   "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" ,
   "UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26",
@@ -119,6 +123,34 @@ static void show_data(unsigned long addr, int nbytes, const char *name)
        if (addr < PAGE_OFFSET || addr > -256UL)
                return;
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+       /*
+        * Treating data in general purpose register as an address
+        * and dereferencing it is quite a dangerous behaviour,
+        * especially when it is an address belonging to secure
+        * region or ioremap region, which can lead to external
+        * abort on non-linefetch and can not be protected by
+        * probe_kernel_address.
+        * We need more strict filtering rules
+        */
+
+#ifdef CONFIG_AMLOGIC_SEC
+       /*
+        * filter out secure monitor region
+        */
+       if (addr <= (unsigned long)high_memory)
+               if (within_secmon_region(addr))
+                       return;
+#endif
+
+       /*
+        * filter out ioremap region
+        */
+       if ((addr >= VMALLOC_START) && (addr <= VMALLOC_END))
+               if (!pfn_valid(vmalloc_to_pfn((void *)addr)))
+                       return;
+#endif
+
        printk("\n%s: %#lx:\n", name, addr);
 
        /*
index 7f377c1..517bec0 100644 (file)
@@ -62,6 +62,10 @@ unsigned long __stack_chk_guard __read_mostly;
 EXPORT_SYMBOL(__stack_chk_guard);
 #endif
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+#include <linux/amlogic/secmon.h>
+#endif
+
 /*
  * Function pointers to optional machine specific functions
  */
@@ -195,6 +199,27 @@ static void show_data(unsigned long addr, int nbytes, const char *name)
        if (addr < PAGE_OFFSET || addr > -256UL)
                return;
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+       /*
+        * Treating data in general purpose register as an address
+        * and dereferencing it is quite a dangerous behaviour,
+        * especially when it belongs to secure monotor region or
+        * ioremap region(for arm64 vmalloc region is already filtered
+        * out), which can lead to external abort on non-linefetch and
+        * can not be protected by probe_kernel_address.
+        * We need more strict filtering rules
+        */
+
+#ifdef CONFIG_AMLOGIC_SEC
+       /*
+        * filter out secure monitor region
+        */
+       if (addr <= (unsigned long)high_memory)
+               if (within_secmon_region(addr))
+                       return;
+#endif
+#endif
+
        printk("\n%s: %#lx:\n", name, addr);
 
        /*
index 36ce744..87a2ce9 100644 (file)
@@ -35,6 +35,8 @@ static void __iomem *sharemem_in_base;
 static void __iomem *sharemem_out_base;
 static long phy_in_base;
 static long phy_out_base;
+static unsigned long secmon_start_virt;
+
 #ifdef CONFIG_ARM64
 #define IN_SIZE        0x1000
 #else
@@ -55,6 +57,19 @@ static long get_sharemem_info(unsigned int function_id)
 }
 
 #define RESERVE_MEM_SIZE       0x300000
+
+int within_secmon_region(unsigned long addr)
+{
+       if (!secmon_start_virt)
+               return 0;
+
+       if ((addr >= secmon_start_virt) &&
+           (addr <= (secmon_start_virt + RESERVE_MEM_SIZE)))
+               return 1;
+
+       return 0;
+}
+
 static int secmon_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
@@ -87,6 +102,7 @@ static int secmon_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
        pr_info("get page:%p, %lx\n", page, page_to_pfn(page));
+       secmon_start_virt = (unsigned long)page_to_virt(page);
 
        if (pfn_valid(__phys_to_pfn(phy_in_base)))
                sharemem_in_base = (void __iomem *)__phys_to_virt(phy_in_base);
index 649d930..359fbd5 100644 (file)
@@ -258,13 +258,6 @@ static DECLARE_WORK(sysrq_showallcpus, sysrq_showregs_othercpus);
 static void sysrq_handle_showallcpus(int key)
 {
        /*
-        * Temporarily disable this function
-        * Remove it later
-        */
-#ifdef CONFIG_AMLOGIC_MODIFY
-       return;
-#endif
-       /*
         * Fall back to the workqueue based printing if the
         * backtrace printing did not succeed or the
         * architecture has no support for it:
index 2003ac2..183d21e 100644 (file)
@@ -27,4 +27,6 @@ void sharemem_mutex_lock(void);
 void sharemem_mutex_unlock(void);
 void secmon_clear_cma_mmu(void);
 
+int within_secmon_region(unsigned long addr);
+
 #endif
index 7555475..0fc9803 100644 (file)
@@ -93,9 +93,19 @@ bool nmi_cpu_backtrace(struct pt_regs *regs)
                                cpu, instruction_pointer(regs));
                } else {
                        pr_warn("NMI backtrace for cpu %d\n", cpu);
+                       /*
+                        * two reasons for not calling show_regs here
+                        * 1. two many logs(100 lines per second) are
+                        *    introduced, which makes the wanted stack
+                        *    infos missed
+                        * 2. leads to potential external abort on
+                        *    non-linefetch issue
+                        */
+#ifndef CONFIG_AMLOGIC_MODIFY
                        if (regs)
                                show_regs(regs);
                        else
+#endif
                                dump_stack();
                }
                cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask));
index 101aeeb..5928204 100644 (file)
@@ -254,10 +254,14 @@ struct page *vmalloc_to_page(const void *vmalloc_addr)
         */
        if (!pgd_none(*pgd)) {
                pud_t *pud = pud_offset(pgd, addr);
+#ifndef CONFIG_AMLOGIC_MODIFY
                WARN_ON_ONCE(pud_bad(*pud));
+#endif
                if (!pud_none(*pud) && !pud_bad(*pud)) {
                        pmd_t *pmd = pmd_offset(pud, addr);
+#ifndef CONFIG_AMLOGIC_MODIFY
                        WARN_ON_ONCE(pmd_bad(*pmd));
+#endif
                        if (!pmd_none(*pmd) && !pmd_bad(*pmd)) {
                                pte_t *ptep, pte;