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@mba.ocn.ne.jp>
---
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,
|