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
next reply other threads:[~2026-08-17 0:03 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-17 0:02 Karl Mehltretter [this message]
2026-08-17 9:15 ` [PATCH] arm64: compat: Keep alignment address arithmetic 32-bit Arnd Bergmann
2026-08-18 3:22 ` Karl Mehltretter
2026-08-18 7:31 ` Arnd Bergmann
2026-08-19 4:56 ` Karl Mehltretter
2026-08-19 11:19 ` Arnd Bergmann
2026-08-19 18:55 ` Karl Mehltretter
2026-08-19 20:27 ` Arnd Bergmann
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.