summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjmallett <jmallett@FreeBSD.org>2010-07-29 02:32:21 +0000
committerjmallett <jmallett@FreeBSD.org>2010-07-29 02:32:21 +0000
commitce09b77dffc110690f8cc8729e3ad2da62d90415 (patch)
tree3d30464b5797d4a0d1356071e6106a87e7321687
parentb6b6deae713ea8abc1a351f07286e1bf6d574179 (diff)
downloadFreeBSD-src-ce09b77dffc110690f8cc8729e3ad2da62d90415.zip
FreeBSD-src-ce09b77dffc110690f8cc8729e3ad2da62d90415.tar.gz
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
-rw-r--r--sys/mips/mips/pm_machdep.c30
-rw-r--r--sys/mips/mips/trap.c161
2 files changed, 126 insertions, 65 deletions
diff --git a/sys/mips/mips/pm_machdep.c b/sys/mips/mips/pm_machdep.c
index 4984ffa..c9a79d8 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 fc255ea..9ba77f8 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 @@ dofault:
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 @@ dofault:
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 @@ dofault:
#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 @@ dofault:
}
}
#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 @@ dofault:
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 @@ dofault:
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 @@ err:
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))
OpenPOWER on IntegriCloud