From 04fedcc7e8d7c2a7adb155f87dd230bd1613b509 Mon Sep 17 00:00:00 2001 From: Jiamin Ma Date: Tue, 30 Apr 2019 16:07:17 +0800 Subject: [PATCH] debug: add more strict checking for show_regs [2/2] 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 --- arch/arm/kernel/process.c | 32 ++++++++++++++++++++++++++++++++ arch/arm64/kernel/process.c | 25 +++++++++++++++++++++++++ drivers/amlogic/secmon/secmon.c | 16 ++++++++++++++++ include/linux/amlogic/secmon.h | 2 ++ lib/nmi_backtrace.c | 10 ++++++++++ mm/vmalloc.c | 4 ++++ 6 files changed, 89 insertions(+) diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 324b32b2ed67..0fb308a468c2 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -47,6 +47,10 @@ unsigned long __stack_chk_guard __read_mostly; EXPORT_SYMBOL(__stack_chk_guard); #endif +#ifdef CONFIG_AMLOGIC_MODIFY +#include +#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); /* diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index bd09799f6c9e..d25a77196631 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -62,6 +62,10 @@ unsigned long __stack_chk_guard __read_mostly; EXPORT_SYMBOL(__stack_chk_guard); #endif +#ifdef CONFIG_AMLOGIC_MODIFY +#include +#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); /* diff --git a/drivers/amlogic/secmon/secmon.c b/drivers/amlogic/secmon/secmon.c index 36ce7443352d..87a2ce96a72b 100644 --- a/drivers/amlogic/secmon/secmon.c +++ b/drivers/amlogic/secmon/secmon.c @@ -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); diff --git a/include/linux/amlogic/secmon.h b/include/linux/amlogic/secmon.h index 2003ac2ee798..183d21e488be 100644 --- a/include/linux/amlogic/secmon.h +++ b/include/linux/amlogic/secmon.h @@ -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 diff --git a/lib/nmi_backtrace.c b/lib/nmi_backtrace.c index 75554754eadf..0fc9803d923b 100644 --- a/lib/nmi_backtrace.c +++ b/lib/nmi_backtrace.c @@ -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)); diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 1323e1af39fd..96086b7c490c 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -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;