From b0c8f6e30fd7a746bb6618510ebd41905076e690 Mon Sep 17 00:00:00 2001 From: tao zeng Date: Mon, 11 Dec 2017 10:08:52 +0800 Subject: [PATCH] mm: show more informations when catch user fault PD#154260: mm: show more informations when catch user fault 1. show pfn for all registers; 2. show memory around all registers; 3. show vma maps around pc/lr. Change-Id: Ibc92f3aea2727c749c3e865495ca7a4920d4e7a0 Signed-off-by: tao zeng --- arch/arm64/Kconfig.debug | 8 ++ arch/arm64/configs/meson64_defconfig | 1 + arch/arm64/include/asm/system_misc.h | 7 + arch/arm64/kernel/process.c | 187 +++++++++++++++++++++++++++ arch/arm64/kernel/traps.c | 3 + arch/arm64/mm/fault.c | 79 +++++++++++ 6 files changed, 285 insertions(+) diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug index b661fe742615..c08609862938 100644 --- a/arch/arm64/Kconfig.debug +++ b/arch/arm64/Kconfig.debug @@ -15,6 +15,14 @@ config ARM64_PTDUMP If in doubt, say N. +config AMLOGIC_USER_FAULT + bool "Amlogic user fault helper" + help + show more information when catch user fault. Including: + memory around all registers + maps with pc/lr + pfn for all virtual address of regirsters + config PID_IN_CONTEXTIDR bool "Write the current PID to the CONTEXTIDR register" help diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index 032ac081e5fe..bc5edf2f1787 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -530,6 +530,7 @@ CONFIG_DEBUG_ATOMIC_SLEEP=y CONFIG_FTRACE_SYSCALLS=y CONFIG_STACK_TRACER=y CONFIG_FUNCTION_PROFILER=y +CONFIG_AMLOGIC_USER_FAULT=y CONFIG_SECURITY_DMESG_RESTRICT=y CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h index bc812435bc76..7f9f2eb1f7ad 100644 --- a/arch/arm64/include/asm/system_misc.h +++ b/arch/arm64/include/asm/system_misc.h @@ -41,6 +41,13 @@ void hook_debug_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct mm_struct; extern void show_pte(struct mm_struct *mm, unsigned long addr); +#ifdef CONFIG_AMLOGIC_USER_FAULT +extern void show_all_pfn(struct task_struct *task, struct pt_regs *regs); +#else +static inline void show_all_pfn(struct task_struct *task, struct pt_regs *regs) +{ +} +#endif /* CONFIG_AMLOGIC_USER_FAULT */ extern void __show_regs(struct pt_regs *); extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 2a474166d384..0082bd44386a 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -212,6 +212,51 @@ static void show_data(unsigned long addr, int nbytes, const char *name) } } +#ifdef CONFIG_AMLOGIC_USER_FAULT +/* + * dump a block of user memory from around the given address + */ +static void show_user_data(unsigned long addr, int nbytes, const char *name) +{ + int i, j; + int nlines; + u32 *p; + + if (!access_ok(VERIFY_READ, (void *)addr, nbytes)) + return; + + pr_info("\n%s: %#lx:\n", name, addr); + + /* + * round address down to a 32 bit boundary + * and always dump a multiple of 32 bytes + */ + p = (u32 *)(addr & ~(sizeof(u32) - 1)); + nbytes += (addr & (sizeof(u32) - 1)); + nlines = (nbytes + 31) / 32; + + for (i = 0; i < nlines; i++) { + /* + * just display low 16 bits of address to keep + * each line of the dump < 80 characters + */ + pr_info("%04lx ", (unsigned long)p & 0xffff); + for (j = 0; j < 8; j++) { + u32 data; + int bad; + + bad = __get_user(data, p); + if (bad) + pr_info(" ********"); + else + pr_info(" %08x", data); + ++p; + } + pr_info("\n"); + } +} +#endif /* CONFIG_AMLOGIC_USER_FAULT */ + static void show_extra_register_data(struct pt_regs *regs, int nbytes) { mm_segment_t fs; @@ -230,6 +275,138 @@ static void show_extra_register_data(struct pt_regs *regs, int nbytes) set_fs(fs); } +#ifdef CONFIG_AMLOGIC_USER_FAULT +static void show_user_extra_register_data(struct pt_regs *regs, int nbytes) +{ + mm_segment_t fs; + unsigned int i, top_reg; + u64 sp, lr; + + if (compat_user_mode(regs)) { + lr = regs->compat_lr; + sp = regs->compat_sp; + top_reg = 13; + } else { + lr = regs->regs[30]; + sp = regs->sp; + top_reg = 29; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + show_user_data(regs->pc - nbytes, nbytes * 2, "PC"); + show_user_data(lr - nbytes, nbytes * 2, "LR"); + show_user_data(sp - nbytes, nbytes * 2, "SP"); + for (i = 0; i < top_reg; i++) { + char name[4]; + + snprintf(name, sizeof(name), "r%u", i); + show_user_data(regs->regs[i] - nbytes, nbytes * 2, name); + } + set_fs(fs); +} + +static void show_vma(struct mm_struct *mm, unsigned long addr) +{ + struct vm_area_struct *vma; + struct file *file; + vm_flags_t flags; + unsigned long ino = 0; + unsigned long long pgoff = 0; + unsigned long start, end; + dev_t dev = 0; + const char *name = NULL; + + vma = find_vma(mm, addr); + if (!vma) { + pr_info("can't find vma for %lx\n", addr); + return; + } + + file = vma->vm_file; + flags = vma->vm_flags; + if (file) { + struct inode *inode = file_inode(vma->vm_file); + + dev = inode->i_sb->s_dev; + ino = inode->i_ino; + pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; + } + + /* We don't show the stack guard page in /proc/maps */ + start = vma->vm_start; + end = vma->vm_end; + + pr_info("vma for %lx:\n", addr); + pr_info("%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ", + start, + end, + flags & VM_READ ? 'r' : '-', + flags & VM_WRITE ? 'w' : '-', + flags & VM_EXEC ? 'x' : '-', + flags & VM_MAYSHARE ? 's' : 'p', + pgoff, + MAJOR(dev), MINOR(dev), ino); + + /* + * Print the dentry name for named mappings, and a + * special [heap] marker for the heap: + */ + if (file) { + char file_name[256] = {}; + char *p = d_path(&file->f_path, file_name, 256); + + if (!IS_ERR(p)) { + mangle_path(file_name, p, "\n"); + pr_info("%s", p); + } else + pr_info(" get file path failed\n"); + goto done; + } + + name = arch_vma_name(vma); + if (!name) { + pid_t tid; + + if (!mm) { + name = "[vdso]"; + goto done; + } + + if (vma->vm_start <= mm->brk && + vma->vm_end >= mm->start_brk) { + name = "[heap]"; + goto done; + } + + tid = vma_is_stack_for_current(vma); + + if (tid != 0) { + /* + * Thread stack in /proc/PID/task/TID/maps or + * the main process stack. + */ + if ((vma->vm_start <= mm->start_stack && + vma->vm_end >= mm->start_stack)) { + name = "[stack]"; + } else { + /* Thread stack in /proc/PID/maps */ + pr_info("[stack:%d]", tid); + } + goto done; + } + + if (vma_get_anon_name(vma)) + pr_info("[anon]"); + } + +done: + if (name) + pr_info("%s", name); + pr_info("\n"); +} +#endif /* CONFIG_AMLOGIC_USER_FAULT */ + void __show_regs(struct pt_regs *regs) { int i, top_reg; @@ -248,6 +425,12 @@ void __show_regs(struct pt_regs *regs) show_regs_print_info(KERN_DEFAULT); print_symbol("PC is at %s\n", instruction_pointer(regs)); print_symbol("LR is at %s\n", lr); +#ifdef CONFIG_AMLOGIC_USER_FAULT + if (user_mode(regs)) { + show_vma(current->mm, instruction_pointer(regs)); + show_vma(current->mm, lr); + } +#endif /* CONFIG_AMLOGIC_USER_FAULT */ printk("pc : [<%016llx>] lr : [<%016llx>] pstate: %08llx\n", regs->pc, lr, regs->pstate); printk("sp : %016llx\n", sp); @@ -267,6 +450,10 @@ void __show_regs(struct pt_regs *regs) } if (!user_mode(regs)) show_extra_register_data(regs, 128); +#ifdef CONFIG_AMLOGIC_USER_FAULT + else + show_user_extra_register_data(regs, 128); +#endif /* CONFIG_AMLOGIC_USER_FAULT */ printk("\n"); } diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index dc1c9fc2d9a2..e236bcab39f6 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -625,6 +625,9 @@ asmlinkage void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr) void __user *pc = (void __user *)instruction_pointer(regs); console_verbose(); +#ifdef CONFIG_AMLOGIC_USER_FAULT + show_all_pfn(current, regs); +#endif /* CONFIG_AMLOGIC_USER_FAULT */ pr_crit("Bad EL0 synchronous exception detected on CPU%d, code 0x%08x -- %s\n", smp_processor_id(), esr, esr_get_class_string(esr)); __show_regs(regs); diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index bc463b5881b0..e680ae190553 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -168,6 +168,43 @@ int ptep_set_access_flags(struct vm_area_struct *vma, } #endif +#ifdef CONFIG_AMLOGIC_USER_FAULT +static long get_user_pfn(struct mm_struct *mm, unsigned long addr) +{ + long pfn = -1; + pgd_t *pgd; + + if (!mm) + mm = &init_mm; + + pgd = pgd_offset(mm, addr); + + do { + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + if (pgd_none(*pgd) || pgd_bad(*pgd)) + break; + + pud = pud_offset(pgd, addr); + if (pud_none(*pud) || pud_bad(*pud)) + break; + + pmd = pmd_offset(pud, addr); + if (pmd_none(*pmd) || pmd_bad(*pmd)) + break; + + pte = pte_offset_map(pmd, addr); + pfn = pte_pfn(*pte); + pte_unmap(pte); + } while (0); + + return pfn; +} +#endif /* CONFIG_AMLOGIC_USER_FAULT */ + + static bool is_el1_instruction_abort(unsigned int esr) { return ESR_ELx_EC(esr) == ESR_ELx_EC_IABT_CUR; @@ -200,6 +237,45 @@ static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr, do_exit(SIGKILL); } +#ifdef CONFIG_AMLOGIC_USER_FAULT +void show_all_pfn(struct task_struct *task, struct pt_regs *regs) +{ + int i; + long pfn1; + char s1[10]; + int top; + + if (compat_user_mode(regs)) + top = 15; + else + top = 31; + pr_info("reg value pfn "); + pr_info("reg value pfn\n"); + for (i = 0; i < top; i++) { + pfn1 = get_user_pfn(task->mm, regs->regs[i]); + if (pfn1 >= 0) + sprintf(s1, "%8lx", pfn1); + else + sprintf(s1, "--------"); + pr_info("r%-2d: %016llx %s ", i, regs->regs[i], s1); + if (i % 2 == 1) + pr_info("\n"); + } + pfn1 = get_user_pfn(task->mm, regs->pc); + if (pfn1 >= 0) + sprintf(s1, "%8lx", pfn1); + else + sprintf(s1, "--------"); + pr_info("pc : %016llx %s\n", regs->pc, s1); + pfn1 = get_user_pfn(task->mm, regs->sp); + if (pfn1 >= 0) + sprintf(s1, "%8lx", pfn1); + else + sprintf(s1, "--------"); + pr_info("sp : %016llx %s\n", regs->sp, s1); +} +#endif /* CONFIG_AMLOGIC_USER_FAULT */ + /* * Something tried to access memory that isn't in our memory map. User mode * accesses just cause a SIGSEGV @@ -217,6 +293,9 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr, tsk->comm, task_pid_nr(tsk), inf->name, sig, addr, esr); show_pte(tsk->mm, addr); + #ifdef CONFIG_AMLOGIC_USER_FAULT + show_all_pfn(tsk, regs); + #endif /* CONFIG_AMLOGIC_USER_FAULT */ show_regs(regs); }