Author: jmallett
Date: Thu Jul 29 02:32:21 2010
New Revision: 210595
URL: http://svn.freebsd.org/changeset/base/210595

Log:
  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

Modified:
  head/sys/mips/mips/pm_machdep.c
  head/sys/mips/mips/trap.c

Modified: head/sys/mips/mips/pm_machdep.c
==============================================================================
--- head/sys/mips/mips/pm_machdep.c     Thu Jul 29 02:24:21 2010        
(r210594)
+++ head/sys/mips/mips/pm_machdep.c     Thu Jul 29 02:32:21 2010        
(r210595)
@@ -479,9 +479,37 @@ exec_setregs(struct thread *td, struct i
        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 |

Modified: head/sys/mips/mips/trap.c
==============================================================================
--- head/sys/mips/mips/trap.c   Thu Jul 29 02:24:21 2010        (r210594)
+++ head/sys/mips/mips/trap.c   Thu Jul 29 02:32:21 2010        (r210595)
@@ -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 tra
  * 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 trapfram
                 * 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))
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to