x86/mpx: Do proper get_user() when running 32-bit binaries on 64-bit kernels
[cascardo/linux.git] / arch / x86 / mm / mpx.c
index 134948b..6127c5e 100644 (file)
@@ -237,7 +237,8 @@ bad_opcode:
  */
 siginfo_t *mpx_generate_siginfo(struct pt_regs *regs)
 {
-       const struct bndreg *bndregs, *bndreg;
+       const struct mpx_bndreg_state *bndregs;
+       const struct mpx_bndreg *bndreg;
        siginfo_t *info = NULL;
        struct insn insn;
        uint8_t bndregno;
@@ -258,13 +259,13 @@ siginfo_t *mpx_generate_siginfo(struct pt_regs *regs)
                goto err_out;
        }
        /* get bndregs field from current task's xsave area */
-       bndregs = get_xsave_field_ptr(XSTATE_BNDREGS);
+       bndregs = get_xsave_field_ptr(XFEATURE_MASK_BNDREGS);
        if (!bndregs) {
                err = -EINVAL;
                goto err_out;
        }
        /* now go select the individual register in the set of 4 */
-       bndreg = &bndregs[bndregno];
+       bndreg = &bndregs->bndreg[bndregno];
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info) {
@@ -306,7 +307,7 @@ err_out:
 
 static __user void *mpx_get_bounds_dir(void)
 {
-       const struct bndcsr *bndcsr;
+       const struct mpx_bndcsr *bndcsr;
 
        if (!cpu_feature_enabled(X86_FEATURE_MPX))
                return MPX_INVALID_BOUNDS_DIR;
@@ -315,7 +316,7 @@ static __user void *mpx_get_bounds_dir(void)
         * The bounds directory pointer is stored in a register
         * only accessible if we first do an xsave.
         */
-       bndcsr = get_xsave_field_ptr(XSTATE_BNDCSR);
+       bndcsr = get_xsave_field_ptr(XFEATURE_MASK_BNDCSR);
        if (!bndcsr)
                return MPX_INVALID_BOUNDS_DIR;
 
@@ -489,10 +490,10 @@ out_unmap:
 static int do_mpx_bt_fault(void)
 {
        unsigned long bd_entry, bd_base;
-       const struct bndcsr *bndcsr;
+       const struct mpx_bndcsr *bndcsr;
        struct mm_struct *mm = current->mm;
 
-       bndcsr = get_xsave_field_ptr(XSTATE_BNDCSR);
+       bndcsr = get_xsave_field_ptr(XFEATURE_MASK_BNDCSR);
        if (!bndcsr)
                return -EINVAL;
        /*
@@ -584,6 +585,29 @@ static unsigned long mpx_bd_entry_to_bt_addr(struct mm_struct *mm,
        return bt_addr;
 }
 
+/*
+ * We only want to do a 4-byte get_user() on 32-bit.  Otherwise,
+ * we might run off the end of the bounds table if we are on
+ * a 64-bit kernel and try to get 8 bytes.
+ */
+int get_user_bd_entry(struct mm_struct *mm, unsigned long *bd_entry_ret,
+               long __user *bd_entry_ptr)
+{
+       u32 bd_entry_32;
+       int ret;
+
+       if (is_64bit_mm(mm))
+               return get_user(*bd_entry_ret, bd_entry_ptr);
+
+       /*
+        * Note that get_user() uses the type of the *pointer* to
+        * establish the size of the get, not the destination.
+        */
+       ret = get_user(bd_entry_32, (u32 __user *)bd_entry_ptr);
+       *bd_entry_ret = bd_entry_32;
+       return ret;
+}
+
 /*
  * Get the base of bounds tables pointed by specific bounds
  * directory entry.
@@ -604,7 +628,7 @@ static int get_bt_addr(struct mm_struct *mm,
                int need_write = 0;
 
                pagefault_disable();
-               ret = get_user(bd_entry, bd_entry_ptr);
+               ret = get_user_bd_entry(mm, &bd_entry, bd_entry_ptr);
                pagefault_enable();
                if (!ret)
                        break;