[PATCH] ARC: check user addresses in unaligned access emulation

Jérémy Jean <[email protected]>
Newsgroups org.infradead.lists.linux-snps-arc,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
ARC700 raises an alignment exception before checking access permissions.
Consequently, a userspace load or store using a misaligned kernel address
reaches misaligned_fixup() before the processor rejects it.

misaligned_fixup() decodes the instruction and repeats the access using
byte loads or stores. These accesses run in kernel mode, and exception
tables only handle accesses that fault. A mapped kernel address is
therefore read or written with supervisor permissions.

Use access_ok() to reject addresses outside the user range before calling
the helpers. Check the whole 2- or 4-byte range and use the checked address
for the emulated operation.

Fixes: 2e651ea1596b ("ARC: Unaligned access emulation")
Assisted-by: Codex:gpt-daybreak-blue
Signed-off-by: Jérémy Jean <[email protected]>
---
 arch/arc/kernel/unaligned.c | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/arch/arc/kernel/unaligned.c b/arch/arc/kernel/unaligned.c
index 3b2d8b1bd271..f6079dc89a6d 100644
--- a/arch/arc/kernel/unaligned.c
+++ b/arch/arc/kernel/unaligned.c
@@ -133,6 +133,8 @@ int no_unaligned_warning __read_mostly = 1;	/* Only 1 warning by default */
 static void fixup_load(struct disasm_state *state, struct pt_regs *regs,
 			struct callee_regs *cregs)
 {
+	unsigned long address;
+	unsigned int size;
 	int val;
 
 	/* register write back */
@@ -143,10 +145,15 @@ static void fixup_load(struct disasm_state *state, struct pt_regs *regs,
 			state->src2 = 0;
 	}
 
+	address = state->src1 + state->src2;
+	size = state->zz ? 2 : 4;
+	if (!access_ok((void __user *)address, size))
+		goto fault;
+
 	if (state->zz == 0) {
-		get32_unaligned_check(val, state->src1 + state->src2);
+		get32_unaligned_check(val, address);
 	} else {
-		get16_unaligned_check(val, state->src1 + state->src2);
+		get16_unaligned_check(val, address);
 
 		if (state->x)
 			val = (val << 16) >> 16;
@@ -163,6 +170,9 @@ fault:	state->fault = 1;
 static void fixup_store(struct disasm_state *state, struct pt_regs *regs,
 			struct callee_regs *cregs)
 {
+	unsigned long address;
+	unsigned int size;
+
 	/* register write back */
 	if ((state->aa == 1) || (state->aa == 2)) {
 		set_reg(state->wb_reg, state->src2 + state->src3, regs, cregs);
@@ -181,11 +191,16 @@ static void fixup_store(struct disasm_state *state, struct pt_regs *regs,
 		}
 	}
 
+	address = state->src2 + state->src3;
+	size = state->zz ? 2 : 4;
+	if (!access_ok((void __user *)address, size))
+		goto fault;
+
 	/* write fix-up */
 	if (!state->zz)
-		put32_unaligned_check(state->src1, state->src2 + state->src3);
+		put32_unaligned_check(state->src1, address);
 	else
-		put16_unaligned_check(state->src1, state->src2 + state->src3);
+		put16_unaligned_check(state->src1, address);
 
 	return;
 
-- 
2.47.3


_______________________________________________
linux-snps-arc mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-snps-arc
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.