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