Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Karl Mehltretter <kmehltretter@gmail.com>
To: Catalin Marinas <catalin.marinas@arm.com>, Will Deacon <will@kernel.org>
Cc: Karl Mehltretter <kmehltretter@gmail.com>,
	Mark Rutland <mark.rutland@arm.com>,
	Arnd Bergmann <arnd@arndb.de>, Ard Biesheuvel <ardb@kernel.org>,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH] arm64: compat: Keep alignment address arithmetic 32-bit
Date: Mon, 17 Aug 2026 02:02:31 +0200	[thread overview]
Message-ID: <20260817000231.21311-1-kmehltretter@gmail.com> (raw)

The compat alignment emulator inherited unsigned long data addresses
from the 32-bit ARM implementation. On arm64, negating the unsigned int
transfer size wraps it at 32 bits before it is added to a 64-bit
address. A decrementing LDM or STM therefore adds nearly 4 GiB instead
of subtracting its transfer size. The resulting address lies outside
the compat task's address space, so the access fails and the process
gets a spurious SIGBUS instead of the fixup.

Using 64-bit addresses also prevents transfer and writeback arithmetic
from wrapping at the AArch32 address-space boundary.

Use 32-bit types for emulated data addresses and offsets, and convert
them with compat_ptr() at the uaccess boundary. This preserves AArch32
modulo-2^32 address generation for every supported transfer form.

Fixes: 3fc24ef32d3b ("arm64: compat: Implement misalignment fixups for multiword loads")
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
Tested:
  Built a static AArch32 alignment test. It deliberately executes unaligned
  multiword instructions. A 64-bit BusyBox initramfs runs it on arm64 QEMU
  and Pi 400, exercising compat_alignment.c.

  On arm64 QEMU and Pi 400, the old kernel gets SIGBUS for ARM LDMDA,
  STMDA, LDMDB, STMDB, Thumb-2 LDMDB/STMDB, and Thumb-1 PUSH. With this
  change, all 18 cases pass.

  The LDRD/STRD register-offset wrap test also passes on both arm64
  targets.

  Native ARM32 QEMU: all runnable cases pass. The high-address wrap probes
  are skipped because of the 3G/1G user/kernel split.

 arch/arm64/kernel/compat_alignment.c | 30 ++++++++++++++++------------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/arch/arm64/kernel/compat_alignment.c b/arch/arm64/kernel/compat_alignment.c
index b68e1d328d4cb..c926041ee97b6 100644
--- a/arch/arm64/kernel/compat_alignment.c
+++ b/arch/arm64/kernel/compat_alignment.c
@@ -2,6 +2,7 @@
 // based on arch/arm/mm/alignment.c
 
 #include <linux/compiler.h>
+#include <linux/compat.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -41,8 +42,8 @@
 	(((hi16) & 0xe000) == 0xe000 && ((hi16) & 0x1800))
 
 union offset_union {
-	unsigned long un;
-	  signed long sn;
+	u32 un;
+	s32 sn;
 };
 
 #define TYPE_ERROR	0
@@ -51,7 +52,7 @@ union offset_union {
 #define TYPE_DONE	3
 
 static void
-do_alignment_finish_ldst(unsigned long addr, u32 instr, struct pt_regs *regs,
+do_alignment_finish_ldst(u32 addr, u32 instr, struct pt_regs *regs,
 			 union offset_union offset)
 {
 	if (!LDST_U_BIT(instr))
@@ -65,7 +66,7 @@ do_alignment_finish_ldst(unsigned long addr, u32 instr, struct pt_regs *regs,
 }
 
 static int
-do_alignment_ldrdstrd(unsigned long addr, u32 instr, struct pt_regs *regs)
+do_alignment_ldrdstrd(u32 addr, u32 instr, struct pt_regs *regs)
 {
 	unsigned int rd = RD_BITS(instr);
 	unsigned int rd2;
@@ -85,14 +86,15 @@ do_alignment_ldrdstrd(unsigned long addr, u32 instr, struct pt_regs *regs)
 	if (load) {
 		unsigned int val, val2;
 
-		if (get_user(val, (u32 __user *)addr) ||
-		    get_user(val2, (u32 __user *)(addr + 4)))
+		if (get_user(val, (u32 __user *)compat_ptr(addr)) ||
+		    get_user(val2, (u32 __user *)compat_ptr(addr + 4)))
 			return TYPE_FAULT;
 		regs->regs[rd] = val;
 		regs->regs[rd2] = val2;
 	} else {
-		if (put_user(regs->regs[rd], (u32 __user *)addr) ||
-		    put_user(regs->regs[rd2], (u32 __user *)(addr + 4)))
+		if (put_user(regs->regs[rd], (u32 __user *)compat_ptr(addr)) ||
+		    put_user(regs->regs[rd2],
+			     (u32 __user *)compat_ptr(addr + 4)))
 			return TYPE_FAULT;
 	}
 	return TYPE_LDST;
@@ -112,10 +114,10 @@ do_alignment_ldrdstrd(unsigned long addr, u32 instr, struct pt_regs *regs)
  * PU = 10             A                    B
  */
 static int
-do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
+do_alignment_ldmstm(u32 addr, u32 instr, struct pt_regs *regs)
 {
 	unsigned int rd, rn, nr_regs, regbits;
-	unsigned long eaddr, newaddr;
+	u32 eaddr, newaddr;
 	unsigned int val;
 
 	/* count the number of registers in the mask to be transferred */
@@ -137,7 +139,8 @@ do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
 	     regbits >>= 1, rd += 1)
 		if (regbits & 1) {
 			if (LDST_L_BIT(instr)) {
-				if (get_user(val, (u32 __user *)eaddr))
+				if (get_user(val,
+					     (u32 __user *)compat_ptr(eaddr)))
 					return TYPE_FAULT;
 				if (rd < 15)
 					regs->regs[rd] = val;
@@ -152,7 +155,8 @@ do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
 				 * to refer to PC, just add 8 here.
 				 */
 				val = (rd < 15) ? regs->regs[rd] : regs->pc + 8;
-				if (put_user(val, (u32 __user *)eaddr))
+				if (put_user(val,
+					     (u32 __user *)compat_ptr(eaddr)))
 					return TYPE_FAULT;
 			}
 			eaddr += 4;
@@ -311,7 +315,7 @@ int do_compat_alignment_fixup(unsigned long addr, struct pt_regs *regs)
 {
 	union offset_union offset;
 	unsigned long instrptr;
-	int (*handler)(unsigned long addr, u32 instr, struct pt_regs *regs);
+	int (*handler)(u32 addr, u32 instr, struct pt_regs *regs);
 	unsigned int type;
 	u32 instr = 0;
 	int isize = 4;
-- 
2.53.0



                 reply	other threads:[~2026-08-17  0:03 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260817000231.21311-1-kmehltretter@gmail.com \
    --to=kmehltretter@gmail.com \
    --cc=ardb@kernel.org \
    --cc=arnd@arndb.de \
    --cc=catalin.marinas@arm.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox