If we passed an invalid _and_ unaligned source address to copy_from_user(), the fault handling code miscalculates a length of uncopied bytes and returns a value greater than original length. This also causes an negative buffer overflow and overwrites some bytes just before the destination kernel buffer. This can happen "src_unaligned" case in memcpy.S. If the first load from source buffer was a LDFIRST/LDREST (L[WD][RL]) instruction, it raise an exception and the THREAD_BUADDR will be an aligned address so it will _smaller_ than its real target address. For all case "src" register is smaller than its target load address (ie. the offset of load instruction is always greater then zero), and on the first load instruction "src" is always start of uncopied source buffer, so we can fix the faulted address using the "src" value. Signed-off-by: Atsushi Nemoto <anemo@xxxxxxxxxxxxx> --- diff --git a/arch/mips/lib/memcpy.S b/arch/mips/lib/memcpy.S index a526c62..7b21bc9 100644 --- a/arch/mips/lib/memcpy.S +++ b/arch/mips/lib/memcpy.S @@ -434,6 +434,12 @@ l_exc: nop LOAD t0, THREAD_BUADDR(t0) # t0 is just past last good address nop + /* If src was unaligned, t0 might be _smaller_ then src. Fix it. */ + slt t1, t0, src + beqz t1, 1f + nop + move t0, src +1: SUB len, AT, t0 # len number of uncopied bytes /* * Here's where we rely on src and dst being incremented in tandem,