Since commit a7f4df4e21dd ("MIPS: VDSO: Add implementations of gettimeofday() and clock_gettime()") we have mapped the GIC user page into user memory for use with the VDSO when a GIC is present. This exposes a hardware-provided GIC register interface to user code, albeit a very simple one. Unfortunately despite the simplicity of the register interface this does present an unusual scenario - like many register interfaces, the GIC requires accesses to meet certain criteria. In current systems the GIC can only be accessed by non-atomic 32 bit or 64 bit loads or stores. Any other accesses, for example half-word or byte sized accesses, will generate a bus error. Since user code has access to this register interface it can generate such bus errors easily. The biggest issues with this have already been fixed by earlier commits, but user code is still able to cause the kernel to spam its log with CM error information by simply generating a stream of bus errors. One non-malicious example of this happening is use of crashme, which seems prone to generate all kinds of weird & wonderful accesses to the GIC user page - presumably its base address is commonly left in a GPR following calls to the VDSO. This patch silences such unwanted output in the kernel log by detecting when a bus error was generated by user code accessing the GIC user page, not printing the CM error information & returning MIPS_BE_FATAL_QUIET to quietly deliver a SIGBUS in such cases. If a program does accidentally generate such a bus error & doesn't have a SIGBUS handler registered then we'll still dump its register & stack state when delivering the unhandled signal. If the bus error is somewhat expected, ie. the program has registered a handler for it as crashme will, or is being ptraced, then the result is that we'll print nothing to the kernel log. Signed-off-by: Paul Burton <paul.burton@xxxxxxxxxx> Cc: Ralf Baechle <ralf@xxxxxxxxxxxxxx> Cc: linux-mips@xxxxxxxxxxxxxx --- arch/mips/include/asm/mips-cm.h | 11 ++++++++--- arch/mips/kernel/mips-cm.c | 39 ++++++++++++++++++++++++++++++++++----- arch/mips/kernel/traps.c | 2 +- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/arch/mips/include/asm/mips-cm.h b/arch/mips/include/asm/mips-cm.h index f6231b91b724..650c9db2dee9 100644 --- a/arch/mips/include/asm/mips-cm.h +++ b/arch/mips/include/asm/mips-cm.h @@ -17,6 +17,7 @@ #include <linux/bitops.h> #include <linux/errno.h> +#include <asm/traps.h> /* The base address of the CM GCR block */ extern void __iomem *mips_gcr_base; @@ -51,12 +52,16 @@ extern phys_addr_t __mips_cm_phys_base(void); extern int mips_cm_is64; /** - * mips_cm_error_report - Report CM cache errors + * mips_cm_be_handler - Report CM cache errors */ +struct pt_regs; #ifdef CONFIG_MIPS_CM -extern void mips_cm_error_report(void); +extern int mips_cm_be_handler(struct pt_regs *regs); #else -static inline void mips_cm_error_report(void) {} +static inline int mips_cm_be_handler(struct pt_regs *regs) +{ + return MIPS_BE_FATAL; +} #endif /** diff --git a/arch/mips/kernel/mips-cm.c b/arch/mips/kernel/mips-cm.c index e91c8c4e2eb5..2ccb0a8b7e90 100644 --- a/arch/mips/kernel/mips-cm.c +++ b/arch/mips/kernel/mips-cm.c @@ -14,6 +14,8 @@ #include <asm/mips-cps.h> #include <asm/mipsregs.h> +#include <asm/ptrace.h> +#include <asm/traps.h> void __iomem *mips_gcr_base; void __iomem *mips_cm_l2sync_base; @@ -332,19 +334,44 @@ void mips_cm_unlock_other(void) preempt_enable(); } -void mips_cm_error_report(void) +int mips_cm_be_handler(struct pt_regs *regs) { + phys_addr_t gic_usr_start, gic_usr_end; u64 cm_error, cm_addr, cm_other; unsigned long revision; int ocause, cause; char buf[256]; if (!mips_cm_present()) - return; + return MIPS_BE_FATAL; + + cm_addr = read_gcr_error_addr(); + + /* + * If a GIC is present then we may have exposed its user page directly + * to user code. This can allow user code to generate bus errors by + * performing unsupported access types to the GIC register interface - + * ie. anything other than a regular non-atomic 32 or 64 bit load or + * store. + * + * Such bus errors are essentially harmless & don't justify the kernel + * spamming the log with CM error information. We'll already dump + * information about the process when appropriate if it doesn't handle + * the resulting SIGBUS, and if it does (eg. crashme) then we ought not + * to be spamming the kernel log at all. Thus if we detect that the bus + * error was caused by user code accessing the GIC user page we return + * MIPS_BE_FATAL_QUIET to quietly deliver a SIGBUS. + */ + if (user_mode(regs) && mips_gic_present()) { + gic_usr_start = virt_to_phys(mips_gic_base) + MIPS_GIC_USER_OFS; + gic_usr_end = gic_usr_start + MIPS_GIC_USER_SZ; + + if ((cm_addr >= gic_usr_start) && (cm_addr < gic_usr_end)) + return MIPS_BE_FATAL_QUIET; + } revision = mips_cm_revision(); cm_error = read_gcr_error_cause(); - cm_addr = read_gcr_error_addr(); cm_other = read_gcr_error_mult(); if (revision < CM_REV_CM3) { /* CM2 */ @@ -352,7 +379,7 @@ void mips_cm_error_report(void) ocause = cm_other >> __ffs(CM_GCR_ERROR_MULT_ERR2ND); if (!cause) - return; + return MIPS_BE_FATAL; if (cause < 16) { unsigned long cca_bits = (cm_error >> 15) & 7; @@ -395,7 +422,7 @@ void mips_cm_error_report(void) ocause = cm_other >> __ffs(CM_GCR_ERROR_MULT_ERR2ND); if (!cause) - return; + return MIPS_BE_FATAL; /* Used by cause == {1,2,3} */ core_id_bits = (cm_error >> 22) & 0xf; @@ -459,4 +486,6 @@ void mips_cm_error_report(void) /* reprime cause register */ write_gcr_error_cause(0); + + return MIPS_BE_FATAL; } diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 2f4546e4abdb..36f21e1f0488 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -454,7 +454,7 @@ asmlinkage void do_be(struct pt_regs *regs) else if (fixup) action = MIPS_BE_FIXUP; else - mips_cm_error_report(); + action = mips_cm_be_handler(regs); switch (action) { case MIPS_BE_DISCARD: -- 2.14.1