|
@@ -64,7 +64,7 @@ static int fpu_emu(struct pt_regs *, struct mips_fpu_struct *,
|
|
|
|
|
|
#if __mips >= 4 && __mips != 32
|
|
|
static int fpux_emu(struct pt_regs *,
|
|
|
- struct mips_fpu_struct *, mips_instruction);
|
|
|
+ struct mips_fpu_struct *, mips_instruction, void *__user *);
|
|
|
#endif
|
|
|
|
|
|
/* Further private data for which no space exists in mips_fpu_struct */
|
|
@@ -208,16 +208,23 @@ static inline int cop1_64bit(struct pt_regs *xcp)
|
|
|
* Two instructions if the instruction is in a branch delay slot.
|
|
|
*/
|
|
|
|
|
|
-static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|
|
+static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|
|
+ void *__user *fault_addr)
|
|
|
{
|
|
|
mips_instruction ir;
|
|
|
unsigned long emulpc, contpc;
|
|
|
unsigned int cond;
|
|
|
|
|
|
- if (get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) {
|
|
|
+ if (!access_ok(VERIFY_READ, xcp->cp0_epc, sizeof(mips_instruction))) {
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = (mips_instruction __user *)xcp->cp0_epc;
|
|
|
return SIGBUS;
|
|
|
}
|
|
|
+ if (__get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) {
|
|
|
+ MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = (mips_instruction __user *)xcp->cp0_epc;
|
|
|
+ return SIGSEGV;
|
|
|
+ }
|
|
|
|
|
|
/* XXX NEC Vr54xx bug workaround */
|
|
|
if ((xcp->cp0_cause & CAUSEF_BD) && !isBranchInstr(&ir))
|
|
@@ -245,10 +252,16 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|
|
#endif
|
|
|
return SIGILL;
|
|
|
}
|
|
|
- if (get_user(ir, (mips_instruction __user *) emulpc)) {
|
|
|
+ if (!access_ok(VERIFY_READ, emulpc, sizeof(mips_instruction))) {
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = (mips_instruction __user *)emulpc;
|
|
|
return SIGBUS;
|
|
|
}
|
|
|
+ if (__get_user(ir, (mips_instruction __user *) emulpc)) {
|
|
|
+ MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = (mips_instruction __user *)emulpc;
|
|
|
+ return SIGSEGV;
|
|
|
+ }
|
|
|
/* __compute_return_epc() will have updated cp0_epc */
|
|
|
contpc = xcp->cp0_epc;
|
|
|
/* In order not to confuse ptrace() et al, tweak context */
|
|
@@ -269,10 +282,17 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|
|
u64 val;
|
|
|
|
|
|
MIPS_FPU_EMU_INC_STATS(loads);
|
|
|
- if (get_user(val, va)) {
|
|
|
+
|
|
|
+ if (!access_ok(VERIFY_READ, va, sizeof(u64))) {
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
return SIGBUS;
|
|
|
}
|
|
|
+ if (__get_user(val, va)) {
|
|
|
+ MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
+ return SIGSEGV;
|
|
|
+ }
|
|
|
DITOREG(val, MIPSInst_RT(ir));
|
|
|
break;
|
|
|
}
|
|
@@ -284,10 +304,16 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|
|
|
|
|
MIPS_FPU_EMU_INC_STATS(stores);
|
|
|
DIFROMREG(val, MIPSInst_RT(ir));
|
|
|
- if (put_user(val, va)) {
|
|
|
+ if (!access_ok(VERIFY_WRITE, va, sizeof(u64))) {
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
return SIGBUS;
|
|
|
}
|
|
|
+ if (__put_user(val, va)) {
|
|
|
+ MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
+ return SIGSEGV;
|
|
|
+ }
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -297,10 +323,16 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|
|
u32 val;
|
|
|
|
|
|
MIPS_FPU_EMU_INC_STATS(loads);
|
|
|
- if (get_user(val, va)) {
|
|
|
+ if (!access_ok(VERIFY_READ, va, sizeof(u32))) {
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
return SIGBUS;
|
|
|
}
|
|
|
+ if (__get_user(val, va)) {
|
|
|
+ MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
+ return SIGSEGV;
|
|
|
+ }
|
|
|
SITOREG(val, MIPSInst_RT(ir));
|
|
|
break;
|
|
|
}
|
|
@@ -312,10 +344,16 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|
|
|
|
|
MIPS_FPU_EMU_INC_STATS(stores);
|
|
|
SIFROMREG(val, MIPSInst_RT(ir));
|
|
|
- if (put_user(val, va)) {
|
|
|
+ if (!access_ok(VERIFY_WRITE, va, sizeof(u32))) {
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
return SIGBUS;
|
|
|
}
|
|
|
+ if (__put_user(val, va)) {
|
|
|
+ MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
+ return SIGSEGV;
|
|
|
+ }
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -440,11 +478,18 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|
|
contpc = (xcp->cp0_epc +
|
|
|
(MIPSInst_SIMM(ir) << 2));
|
|
|
|
|
|
- if (get_user(ir,
|
|
|
- (mips_instruction __user *) xcp->cp0_epc)) {
|
|
|
+ if (!access_ok(VERIFY_READ, xcp->cp0_epc,
|
|
|
+ sizeof(mips_instruction))) {
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = (mips_instruction __user *)xcp->cp0_epc;
|
|
|
return SIGBUS;
|
|
|
}
|
|
|
+ if (__get_user(ir,
|
|
|
+ (mips_instruction __user *) xcp->cp0_epc)) {
|
|
|
+ MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = (mips_instruction __user *)xcp->cp0_epc;
|
|
|
+ return SIGSEGV;
|
|
|
+ }
|
|
|
|
|
|
switch (MIPSInst_OPCODE(ir)) {
|
|
|
case lwc1_op:
|
|
@@ -506,9 +551,8 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|
|
|
|
|
#if __mips >= 4 && __mips != 32
|
|
|
case cop1x_op:{
|
|
|
- int sig;
|
|
|
-
|
|
|
- if ((sig = fpux_emu(xcp, ctx, ir)))
|
|
|
+ int sig = fpux_emu(xcp, ctx, ir, fault_addr);
|
|
|
+ if (sig)
|
|
|
return sig;
|
|
|
break;
|
|
|
}
|
|
@@ -604,7 +648,7 @@ DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg);
|
|
|
DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg);
|
|
|
|
|
|
static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|
|
- mips_instruction ir)
|
|
|
+ mips_instruction ir, void *__user *fault_addr)
|
|
|
{
|
|
|
unsigned rcsr = 0; /* resulting csr */
|
|
|
|
|
@@ -624,10 +668,16 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|
|
xcp->regs[MIPSInst_FT(ir)]);
|
|
|
|
|
|
MIPS_FPU_EMU_INC_STATS(loads);
|
|
|
- if (get_user(val, va)) {
|
|
|
+ if (!access_ok(VERIFY_READ, va, sizeof(u32))) {
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
return SIGBUS;
|
|
|
}
|
|
|
+ if (__get_user(val, va)) {
|
|
|
+ MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
+ return SIGSEGV;
|
|
|
+ }
|
|
|
SITOREG(val, MIPSInst_FD(ir));
|
|
|
break;
|
|
|
|
|
@@ -638,10 +688,16 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|
|
MIPS_FPU_EMU_INC_STATS(stores);
|
|
|
|
|
|
SIFROMREG(val, MIPSInst_FS(ir));
|
|
|
- if (put_user(val, va)) {
|
|
|
+ if (!access_ok(VERIFY_WRITE, va, sizeof(u32))) {
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
return SIGBUS;
|
|
|
}
|
|
|
+ if (put_user(val, va)) {
|
|
|
+ MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
+ return SIGSEGV;
|
|
|
+ }
|
|
|
break;
|
|
|
|
|
|
case madd_s_op:
|
|
@@ -701,10 +757,16 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|
|
xcp->regs[MIPSInst_FT(ir)]);
|
|
|
|
|
|
MIPS_FPU_EMU_INC_STATS(loads);
|
|
|
- if (get_user(val, va)) {
|
|
|
+ if (!access_ok(VERIFY_READ, va, sizeof(u64))) {
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
return SIGBUS;
|
|
|
}
|
|
|
+ if (__get_user(val, va)) {
|
|
|
+ MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
+ return SIGSEGV;
|
|
|
+ }
|
|
|
DITOREG(val, MIPSInst_FD(ir));
|
|
|
break;
|
|
|
|
|
@@ -714,10 +776,16 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|
|
|
|
|
MIPS_FPU_EMU_INC_STATS(stores);
|
|
|
DIFROMREG(val, MIPSInst_FS(ir));
|
|
|
- if (put_user(val, va)) {
|
|
|
+ if (!access_ok(VERIFY_WRITE, va, sizeof(u64))) {
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
return SIGBUS;
|
|
|
}
|
|
|
+ if (__put_user(val, va)) {
|
|
|
+ MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = va;
|
|
|
+ return SIGSEGV;
|
|
|
+ }
|
|
|
break;
|
|
|
|
|
|
case madd_d_op:
|
|
@@ -1242,7 +1310,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|
|
}
|
|
|
|
|
|
int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|
|
- int has_fpu)
|
|
|
+ int has_fpu, void *__user *fault_addr)
|
|
|
{
|
|
|
unsigned long oldepc, prevepc;
|
|
|
mips_instruction insn;
|
|
@@ -1252,10 +1320,16 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|
|
do {
|
|
|
prevepc = xcp->cp0_epc;
|
|
|
|
|
|
- if (get_user(insn, (mips_instruction __user *) xcp->cp0_epc)) {
|
|
|
+ if (!access_ok(VERIFY_READ, xcp->cp0_epc, sizeof(mips_instruction))) {
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = (mips_instruction __user *)xcp->cp0_epc;
|
|
|
return SIGBUS;
|
|
|
}
|
|
|
+ if (__get_user(insn, (mips_instruction __user *) xcp->cp0_epc)) {
|
|
|
+ MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
+ *fault_addr = (mips_instruction __user *)xcp->cp0_epc;
|
|
|
+ return SIGSEGV;
|
|
|
+ }
|
|
|
if (insn == 0)
|
|
|
xcp->cp0_epc += 4; /* skip nops */
|
|
|
else {
|
|
@@ -1267,7 +1341,7 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|
|
*/
|
|
|
/* convert to ieee library modes */
|
|
|
ieee754_csr.rm = ieee_rm[ieee754_csr.rm];
|
|
|
- sig = cop1Emulate(xcp, ctx);
|
|
|
+ sig = cop1Emulate(xcp, ctx, fault_addr);
|
|
|
/* revert to mips rounding mode */
|
|
|
ieee754_csr.rm = mips_rm[ieee754_csr.rm];
|
|
|
}
|