diff --git a/arch/alpha/kernel/err_common.c b/arch/alpha/kernel/err_common.c index 94e1b3dcf6d4..9271ea425a7b 100644 --- a/arch/alpha/kernel/err_common.c +++ b/arch/alpha/kernel/err_common.c @@ -44,6 +44,19 @@ mchk_dump_mem(void *data, size_t length, char **annotation) } } +/* Preserve rejected frames without trusting their internal offsets. */ +void +mchk_dump_logout_frame_raw(struct el_common *header) +{ + size_t length = header->size; + + /* Bound diagnostic output and never read a partial final quadword. */ + if (length > 1024) + length = 1024; + length &= ~7UL; + mchk_dump_mem(header, length, NULL); +} + void mchk_dump_logout_frame(struct el_common *mchk_header) { diff --git a/arch/alpha/kernel/err_ev6.c b/arch/alpha/kernel/err_ev6.c index 8144f2045b5b..f12299ae1f24 100644 --- a/arch/alpha/kernel/err_ev6.c +++ b/arch/alpha/kernel/err_ev6.c @@ -190,6 +190,25 @@ ev6_process_logout_frame(struct el_common *mchk_header, int print) (struct el_common_EV6_mcheck *)mchk_header; int status = MCHK_DISPOSITION_UNKNOWN_ERROR; + /* + * ES40 correctable frames end the CPU area before EXC_ADDR (Table + * D-19). Validate both regions before decoding or dumping any data. + */ + if (mchk_header->proc_offset != offsetof(struct el_common_EV6_mcheck, + I_STAT) || + mchk_header->sys_offset < offsetof(struct el_common_EV6_mcheck, + EXC_ADDR) || + mchk_header->sys_offset > mchk_header->size || + ((mchk_header->sys_offset | mchk_header->size) & 7)) { + if (print) { + printk("%s Invalid EV6 logout frame: size %x, CPU %x, system %x\n", + err_print_prefix, mchk_header->size, + mchk_header->proc_offset, mchk_header->sys_offset); + mchk_dump_logout_frame_raw(mchk_header); + } + return MCHK_DISPOSITION_UNKNOWN_ERROR; + } + status |= ev6_parse_ibox(ev6mchk->I_STAT, print); status |= ev6_parse_mbox(ev6mchk->MM_STAT, ev6mchk->DC_STAT, ev6mchk->C_STAT, print); @@ -203,16 +222,15 @@ ev6_process_logout_frame(struct el_common *mchk_header, int print) if (status != MCHK_DISPOSITION_DISMISS) { char *saved_err_prefix = err_print_prefix; - /* - * Dump some additional information from the frame - */ - printk("%s EXC_ADDR: 0x%016lx IER_CM: 0x%016lx" - " ISUM: 0x%016lx\n" - " PAL_BASE: 0x%016lx I_CTL: 0x%016lx" - " PCTX: 0x%016lx\n", - err_print_prefix, - ev6mchk->EXC_ADDR, ev6mchk->IER_CM, ev6mchk->ISUM, - ev6mchk->PAL_BASE, ev6mchk->I_CTL, ev6mchk->PCTX); + /* These registers are absent from short correctable frames. */ + if (mchk_header->sys_offset >= sizeof(*ev6mchk)) + printk("%s EXC_ADDR: 0x%016lx IER_CM: 0x%016lx" + " ISUM: 0x%016lx\n" + " PAL_BASE: 0x%016lx I_CTL: 0x%016lx" + " PCTX: 0x%016lx\n", + err_print_prefix, + ev6mchk->EXC_ADDR, ev6mchk->IER_CM, ev6mchk->ISUM, + ev6mchk->PAL_BASE, ev6mchk->I_CTL, ev6mchk->PCTX); if (status == MCHK_DISPOSITION_UNKNOWN_ERROR) { printk("%s UNKNOWN error, frame follows:\n", diff --git a/arch/alpha/kernel/err_impl.h b/arch/alpha/kernel/err_impl.h index 737b958a586d..5bef23a00942 100644 --- a/arch/alpha/kernel/err_impl.h +++ b/arch/alpha/kernel/err_impl.h @@ -47,6 +47,7 @@ struct el_subpacket_handler { extern char *err_print_prefix; extern void mchk_dump_mem(void *, size_t, char **); +extern void mchk_dump_logout_frame_raw(struct el_common *header); extern void mchk_dump_logout_frame(struct el_common *); extern void el_print_timestamp(union el_timestamp *); extern void el_process_subpackets(struct el_subpacket *, int);