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
This commit is contained in:
parent
cb534a9a52
commit
864ec37f39
@ -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 |
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user