From 864ec37f3973f0c2e26c8ddc9de7506a8aa7e707 Mon Sep 17 00:00:00 2001 From: Juli Mallett Date: Thu, 29 Jul 2010 02:32:21 +0000 Subject: [PATCH] o) Subtract 64K from the default userland stack pointer. GCC generate code that with a 32-bit ABI on a system with 64-bit registers can attempt to access an invalid (well, kernel) memory address rather than the intended user address for stack-relative loads and stores. Lowering the stack pointer works around this. [1] o) Make TRAP_DEBUG code conditional on the trap_debug variable. Make trap_debug default to 0 instead of 1 now but make it possible to change it at runtime using sysctl. o) Kill programs that attempt an unaligned access of a kernel address. Note that with some ABIs, calling useracc() is not sufficient since the register may be 64-bit but vm_offset_t is 32-bit so a kernel address could be truncated to what looks like a valid user address, allowing the user to crash the kernel. o) Clean up unaligned access emulation to support unaligned 16-bit and 64-bit accesses. (For 16-bit accesses it was checking for user access to too much memory (4 bytes) and there was no 64-bit support.) This still lacks support for unaligned load-linked and store-conditional. Reviewed by: [1] gonzo --- sys/mips/mips/pm_machdep.c | 30 ++++++- sys/mips/mips/trap.c | 161 ++++++++++++++++++++++--------------- 2 files changed, 126 insertions(+), 65 deletions(-) diff --git a/sys/mips/mips/pm_machdep.c b/sys/mips/mips/pm_machdep.c index 4984ffadff1d..c9a79d80d5e5 100644 --- a/sys/mips/mips/pm_machdep.c +++ b/sys/mips/mips/pm_machdep.c @@ -479,9 +479,37 @@ exec_setregs(struct thread *td, struct image_params *imgp, u_long stack) bzero((caddr_t)td->td_frame, sizeof(struct trapframe)); /* - * Make sp 64-bit aligned. + * The stack pointer has to be aligned to accommodate the largest + * datatype at minimum. This probably means it should be 16-byte + * aligned, but for now we're 8-byte aligning it. */ td->td_frame->sp = ((register_t) stack) & ~(sizeof(__int64_t) - 1); + + /* + * If we're running o32 or n32 programs but have 64-bit registers, + * GCC may use stack-relative addressing near the top of user + * address space that, due to sign extension, will yield an + * invalid address. For instance, if sp is 0x7fffff00 then GCC + * might do something like this to load a word from 0x7ffffff0: + * + * addu sp, sp, 32768 + * lw t0, -32528(sp) + * + * On systems with 64-bit registers, sp is sign-extended to + * 0xffffffff80007f00 and the load is instead done from + * 0xffffffff7ffffff0. + * + * To prevent this, we subtract 64K from the stack pointer here. + * + * For consistency, we should just always do this unless we're + * running n64 programs. For now, since we don't support + * COMPAT_FREEBSD32 on n64 kernels, we just do it unless we're + * running n64 kernels. + */ +#if !defined(__mips_n64) + td->td_frame->sp -= 65536; +#endif + td->td_frame->pc = imgp->entry_addr & ~3; td->td_frame->t9 = imgp->entry_addr & ~3; /* abicall req */ td->td_frame->sr = MIPS_SR_KSU_USER | MIPS_SR_EXL | MIPS_SR_INT_IE | diff --git a/sys/mips/mips/trap.c b/sys/mips/mips/trap.c index fc255ea87d12..9ba77f8eb06f 100644 --- a/sys/mips/mips/trap.c +++ b/sys/mips/mips/trap.c @@ -96,7 +96,9 @@ __FBSDID("$FreeBSD$"); #ifdef TRAP_DEBUG -int trap_debug = 1; +int trap_debug = 0; +SYSCTL_INT(_machdep, OID_AUTO, trap_debug, CTLFLAG_RW, + &trap_debug, 0, "Debug information on all traps"); #endif static void log_illegal_instruction(const char *, struct trapframe *); @@ -259,7 +261,7 @@ static int allow_unaligned_acc = 1; SYSCTL_INT(_vm, OID_AUTO, allow_unaligned_acc, CTLFLAG_RW, &allow_unaligned_acc, 0, "Allow unaligned accesses"); -static int emulate_unaligned_access(struct trapframe *frame); +static int emulate_unaligned_access(struct trapframe *frame, int mode); extern void fswintrberr(void); /* XXX */ @@ -555,7 +557,10 @@ trap(struct trapframe *trapframe) case T_ADDR_ERR_LD + T_USER: /* misaligned or kseg access */ case T_ADDR_ERR_ST + T_USER: /* misaligned or kseg access */ - if (allow_unaligned_acc) { + if (trapframe->badvaddr < 0 || + trapframe->badvaddr >= VM_MAXUSER_ADDRESS) { + msg = "ADDRESS_SPACE_ERR"; + } else if (allow_unaligned_acc) { int mode; if (type == (T_ADDR_ERR_LD + T_USER)) @@ -563,23 +568,13 @@ trap(struct trapframe *trapframe) else mode = VM_PROT_WRITE; - /* - * ADDR_ERR faults have higher priority than TLB - * Miss faults. Therefore, it is necessary to - * verify that the faulting address is a valid - * virtual address within the process' address space - * before trying to emulate the unaligned access. - */ - if (useracc((caddr_t) - (((vm_offset_t)trapframe->badvaddr) & - ~(sizeof(int) - 1)), sizeof(int) * 2, mode)) { - access_type = emulate_unaligned_access( - trapframe); - if (access_type != 0) - goto out; - } + access_type = emulate_unaligned_access(trapframe, mode); + if (access_type != 0) + goto out; + msg = "ALIGNMENT_FIX_ERR"; + } else { + msg = "ADDRESS_ERR"; } - msg = "ADDRESS_ERR"; /* FALL THROUGH */ @@ -686,7 +681,9 @@ trap(struct trapframe *trapframe) #endif } #ifdef TRAP_DEBUG - printf("SYSCALL #%d pid:%u\n", code, p->p_pid); + if (trap_debug) { + printf("SYSCALL #%d pid:%u\n", code, p->p_pid); + } #endif if (p->p_sysent->sv_mask) @@ -723,8 +720,10 @@ trap(struct trapframe *trapframe) } } #ifdef TRAP_DEBUG - for (i = 0; i < nargs; i++) { - printf("args[%d] = %#jx\n", i, (intmax_t)args[i]); + if (trap_debug) { + for (i = 0; i < nargs; i++) { + printf("args[%d] = %#jx\n", i, (intmax_t)args[i]); + } } #endif #ifdef SYSCALL_TRACING @@ -937,8 +936,10 @@ trap(struct trapframe *trapframe) case T_ADDR_ERR_LD: /* misaligned access */ case T_ADDR_ERR_ST: /* misaligned access */ #ifdef TRAP_DEBUG - printf("+++ ADDR_ERR: type = %d, badvaddr = %#jx\n", type, - (intmax_t)trapframe->badvaddr); + if (trap_debug) { + printf("+++ ADDR_ERR: type = %d, badvaddr = %#jx\n", type, + (intmax_t)trapframe->badvaddr); + } #endif /* Only allow emulation on a user address */ if (allow_unaligned_acc && @@ -950,22 +951,9 @@ trap(struct trapframe *trapframe) else mode = VM_PROT_WRITE; - /* - * ADDR_ERR faults have higher priority than TLB - * Miss faults. Therefore, it is necessary to - * verify that the faulting address is a valid - * virtual address within the process' address space - * before trying to emulate the unaligned access. - */ - if (useracc((caddr_t) - (((vm_offset_t)trapframe->badvaddr) & - ~(sizeof(int) - 1)), sizeof(int) * 2, mode)) { - access_type = emulate_unaligned_access( - trapframe); - if (access_type != 0) { - return (trapframe->pc); - } - } + access_type = emulate_unaligned_access(trapframe, mode); + if (access_type != 0) + return (trapframe->pc); } /* FALLTHROUGH */ @@ -997,9 +985,10 @@ trap(struct trapframe *trapframe) printf("kernel mode)\n"); #ifdef TRAP_DEBUG - printf("badvaddr = %#jx, pc = %#jx, ra = %#jx, sr = %#jxx\n", - (intmax_t)trapframe->badvaddr, (intmax_t)trapframe->pc, (intmax_t)trapframe->ra, - (intmax_t)trapframe->sr); + if (trap_debug) + printf("badvaddr = %#jx, pc = %#jx, ra = %#jx, sr = %#jxx\n", + (intmax_t)trapframe->badvaddr, (intmax_t)trapframe->pc, (intmax_t)trapframe->ra, + (intmax_t)trapframe->sr); #endif #ifdef KDB @@ -1433,76 +1422,120 @@ log_bad_page_fault(char *msg, struct trapframe *frame, int trap_type) * Unaligned load/store emulation */ static int -mips_unaligned_load_store(struct trapframe *frame, register_t addr, register_t pc) +mips_unaligned_load_store(struct trapframe *frame, int mode, register_t addr, register_t pc) { register_t *reg = (register_t *) frame; u_int32_t inst = *((u_int32_t *)(intptr_t)pc); - u_int32_t value_msb, value; - int access_type = 0; + register_t value_msb, value; + unsigned size; + /* + * ADDR_ERR faults have higher priority than TLB + * Miss faults. Therefore, it is necessary to + * verify that the faulting address is a valid + * virtual address within the process' address space + * before trying to emulate the unaligned access. + */ + switch (MIPS_INST_OPCODE(inst)) { + case OP_LHU: case OP_LH: + case OP_SH: + size = 2; + break; + case OP_LWU: case OP_LW: + case OP_SW: + size = 4; + break; + case OP_LD: + case OP_SD: + size = 8; + break; + default: + printf("%s: unhandled opcode in address error: %#x\n", __func__, MIPS_INST_OPCODE(inst)); + return (0); + } + + if (!useracc((void *)((vm_offset_t)addr & ~(size - 1)), size * 2, mode)) + return (0); + + /* + * XXX + * Handle LL/SC LLD/SCD. + */ switch (MIPS_INST_OPCODE(inst)) { case OP_LHU: + KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction.")); lbu_macro(value_msb, addr); addr += 1; lbu_macro(value, addr); value |= value_msb << 8; reg[MIPS_INST_RT(inst)] = value; - access_type = MIPS_LHU_ACCESS; - break; + return (MIPS_LHU_ACCESS); case OP_LH: + KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction.")); lb_macro(value_msb, addr); addr += 1; lbu_macro(value, addr); value |= value_msb << 8; reg[MIPS_INST_RT(inst)] = value; - access_type = MIPS_LH_ACCESS; - break; + return (MIPS_LH_ACCESS); case OP_LWU: + KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction.")); lwl_macro(value, addr); addr += 3; lwr_macro(value, addr); value &= 0xffffffff; reg[MIPS_INST_RT(inst)] = value; - access_type = MIPS_LWU_ACCESS; - break; + return (MIPS_LWU_ACCESS); case OP_LW: + KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction.")); lwl_macro(value, addr); addr += 3; lwr_macro(value, addr); reg[MIPS_INST_RT(inst)] = value; - access_type = MIPS_LW_ACCESS; - break; + return (MIPS_LW_ACCESS); + + case OP_LD: + KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction.")); + ldl_macro(value, addr); + addr += 7; + ldr_macro(value, addr); + reg[MIPS_INST_RT(inst)] = value; + return (MIPS_LD_ACCESS); case OP_SH: + KASSERT(mode == VM_PROT_WRITE, ("access mode must be write for store instruction.")); value = reg[MIPS_INST_RT(inst)]; value_msb = value >> 8; sb_macro(value_msb, addr); addr += 1; sb_macro(value, addr); - access_type = MIPS_SH_ACCESS; - break; + return (MIPS_SH_ACCESS); case OP_SW: + KASSERT(mode == VM_PROT_WRITE, ("access mode must be write for store instruction.")); value = reg[MIPS_INST_RT(inst)]; swl_macro(value, addr); addr += 3; swr_macro(value, addr); - access_type = MIPS_SW_ACCESS; - break; + return (MIPS_SW_ACCESS); - default: - break; + case OP_SD: + KASSERT(mode == VM_PROT_WRITE, ("access mode must be write for store instruction.")); + value = reg[MIPS_INST_RT(inst)]; + sdl_macro(value, addr); + addr += 7; + sdr_macro(value, addr); + return (MIPS_SD_ACCESS); } - - return access_type; + panic("%s: should not be reached.", __func__); } static int -emulate_unaligned_access(struct trapframe *frame) +emulate_unaligned_access(struct trapframe *frame, int mode) { register_t pc; int access_type = 0; @@ -1523,7 +1556,7 @@ emulate_unaligned_access(struct trapframe *frame) * Otherwise restore pc and fall through. */ access_type = mips_unaligned_load_store(frame, - frame->badvaddr, pc); + mode, frame->badvaddr, pc); if (access_type) { if (DELAYBRANCH(frame->cause))