author | Jiamin Ma <jiamin.ma@amlogic.com> | 2019-04-30 08:07:17 (GMT) |
---|---|---|
committer | Jianxin Pan <jianxin.pan@amlogic.com> | 2019-06-18 05:36:36 (GMT) |
commit | 35ffe6082ff7474ce19b11690dc31d3920401d46 (patch) | |
tree | 23dff6ef32fe5105aa8fb9acee94536c1da42a51 | |
parent | b0c30ab5a4cd0c1af4b9e7385f3c38333905fc77 (diff) | |
download | common-35ffe6082ff7474ce19b11690dc31d3920401d46.zip common-35ffe6082ff7474ce19b11690dc31d3920401d46.tar.gz common-35ffe6082ff7474ce19b11690dc31d3920401d46.tar.bz2 |
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 <jiamin.ma@amlogic.com>
-rw-r--r-- | arch/arm/kernel/process.c | 32 | ||||
-rw-r--r-- | arch/arm64/kernel/process.c | 25 | ||||
-rw-r--r-- | drivers/amlogic/secmon/secmon.c | 16 | ||||
-rw-r--r-- | drivers/tty/sysrq.c | 7 | ||||
-rw-r--r-- | include/linux/amlogic/secmon.h | 2 | ||||
-rw-r--r-- | lib/nmi_backtrace.c | 10 | ||||
-rw-r--r-- | mm/vmalloc.c | 4 |
7 files changed, 89 insertions, 7 deletions
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index c0c1730..7468ede 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 <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); /* diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 7f377c1..517bec0 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 <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); /* diff --git a/drivers/amlogic/secmon/secmon.c b/drivers/amlogic/secmon/secmon.c index 36ce744..87a2ce9 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/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 649d930..359fbd5 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -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: diff --git a/include/linux/amlogic/secmon.h b/include/linux/amlogic/secmon.h index 2003ac2..183d21e 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 7555475..0fc9803d 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 101aeeb..5928204 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; |