* [PATCH RESEND] ARM: alignment: restore uaccess state on emulation fault
@ 2026-08-11 16:45 Karl Mehltretter
2026-08-12 7:28 ` Linus Walleij
0 siblings, 1 reply; 2+ messages in thread
From: Karl Mehltretter @ 2026-08-11 16:45 UTC (permalink / raw)
To: Russell King
Cc: Karl Mehltretter, Linus Walleij, Ard Biesheuvel,
Florian Fainelli, linux-arm-kernel, linux-kernel, stable
The alignment handlers enable access to user memory while emulating
unaligned instructions. If a checked access faults, its exception table
fixup jumps to the handler's fault label and bypasses uaccess_restore().
This leaves DOMAIN_USER as DOMAIN_CLIENT with SW domain PAN, or leaves
TTBR0 page table walks enabled with TTBR0 PAN, while the fault is handled.
An ARM926 QEMU test using LDMIA across an unmapped page boundary reports
DOMAIN_USER=1 at TYPE_FAULT before this change and DOMAIN_USER=0 after it.
SW domain PAN is the default on ARMv5, ARMv6, and non-LPAE ARMv7 builds.
Keep the saved uaccess state at function scope and track whether it was
saved. Restore it at the fault label only when needed. Paths that did not
enable user access continue to skip the restore.
Fixes: 274e91b81ed2 ("ARM: alignment: fix alignment handling for uaccess changes")
Cc: stable@vger.kernel.org
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
Resending after two weeks without feedback; add ARM alignment/PAN
reviewers to Cc.
arch/arm/mm/alignment.c | 51 ++++++++++++++++++++++++++++++-----------
1 file changed, 37 insertions(+), 14 deletions(-)
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index 812380f30ae3..2b470a419dda 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -341,6 +341,8 @@ static int
do_alignment_ldrhstrh(unsigned long addr, u32 instr, struct pt_regs *regs)
{
unsigned int rd = RD_BITS(instr);
+ unsigned int ua_flags;
+ bool ua_saved = false;
ai_half += 1;
@@ -364,10 +366,11 @@ do_alignment_ldrhstrh(unsigned long addr, u32 instr, struct pt_regs *regs)
user:
if (LDST_L_BIT(instr)) {
unsigned long val;
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
get16t_unaligned_check(val, addr);
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
/* signed half-word? */
if (instr & 0x40)
@@ -375,14 +378,17 @@ do_alignment_ldrhstrh(unsigned long addr, u32 instr, struct pt_regs *regs)
regs->uregs[rd] = val;
} else {
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
put16t_unaligned_check(regs->uregs[rd], addr);
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
}
return TYPE_LDST;
fault:
+ if (ua_saved)
+ uaccess_restore(ua_flags);
return TYPE_FAULT;
}
@@ -391,6 +397,8 @@ do_alignment_ldrdstrd(unsigned long addr, u32 instr, struct pt_regs *regs)
{
unsigned int rd = RD_BITS(instr);
unsigned int rd2;
+ unsigned int ua_flags;
+ bool ua_saved = false;
int load;
if ((instr & 0xfe000000) == 0xe8000000) {
@@ -425,26 +433,30 @@ do_alignment_ldrdstrd(unsigned long addr, u32 instr, struct pt_regs *regs)
user:
if (load) {
unsigned long val, val2;
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
get32t_unaligned_check(val, addr);
get32t_unaligned_check(val2, addr + 4);
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
regs->uregs[rd] = val;
regs->uregs[rd2] = val2;
} else {
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
put32t_unaligned_check(regs->uregs[rd], addr);
put32t_unaligned_check(regs->uregs[rd2], addr + 4);
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
}
return TYPE_LDST;
bad:
return TYPE_ERROR;
fault:
+ if (ua_saved)
+ uaccess_restore(ua_flags);
return TYPE_FAULT;
}
@@ -452,6 +464,8 @@ static int
do_alignment_ldrstr(unsigned long addr, u32 instr, struct pt_regs *regs)
{
unsigned int rd = RD_BITS(instr);
+ unsigned int ua_flags;
+ bool ua_saved = false;
ai_word += 1;
@@ -469,18 +483,22 @@ do_alignment_ldrstr(unsigned long addr, u32 instr, struct pt_regs *regs)
trans:
if (LDST_L_BIT(instr)) {
unsigned int val;
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
get32t_unaligned_check(val, addr);
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
regs->uregs[rd] = val;
} else {
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
put32t_unaligned_check(regs->uregs[rd], addr);
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
}
return TYPE_LDST;
fault:
+ if (ua_saved)
+ uaccess_restore(ua_flags);
return TYPE_FAULT;
}
@@ -502,6 +520,8 @@ do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
{
unsigned int rd, rn, correction, nr_regs, regbits;
unsigned long eaddr, newaddr;
+ unsigned int ua_flags;
+ bool ua_saved = false;
if (LDM_S_BIT(instr))
goto bad;
@@ -547,7 +567,8 @@ do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
#endif
if (user_mode(regs)) {
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
regbits >>= 1, rd += 1)
if (regbits & 1) {
@@ -559,7 +580,7 @@ do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
put32t_unaligned_check(regs->uregs[rd], eaddr);
eaddr += 4;
}
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
} else {
for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
regbits >>= 1, rd += 1)
@@ -581,6 +602,8 @@ do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
return TYPE_DONE;
fault:
+ if (ua_saved)
+ uaccess_restore(ua_flags);
regs->ARM_pc -= correction;
return TYPE_FAULT;
--
2.53.0
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH RESEND] ARM: alignment: restore uaccess state on emulation fault
2026-08-11 16:45 [PATCH RESEND] ARM: alignment: restore uaccess state on emulation fault Karl Mehltretter
@ 2026-08-12 7:28 ` Linus Walleij
0 siblings, 0 replies; 2+ messages in thread
From: Linus Walleij @ 2026-08-12 7:28 UTC (permalink / raw)
To: Karl Mehltretter
Cc: Russell King, Ard Biesheuvel, Florian Fainelli, linux-arm-kernel,
linux-kernel, stable
On Tue, Aug 11, 2026 at 6:45 PM Karl Mehltretter <kmehltretter@gmail.com> wrote:
> The alignment handlers enable access to user memory while emulating
> unaligned instructions. If a checked access faults, its exception table
> fixup jumps to the handler's fault label and bypasses uaccess_restore().
> This leaves DOMAIN_USER as DOMAIN_CLIENT with SW domain PAN, or leaves
> TTBR0 page table walks enabled with TTBR0 PAN, while the fault is handled.
>
> An ARM926 QEMU test using LDMIA across an unmapped page boundary reports
> DOMAIN_USER=1 at TYPE_FAULT before this change and DOMAIN_USER=0 after it.
> SW domain PAN is the default on ARMv5, ARMv6, and non-LPAE ARMv7 builds.
>
> Keep the saved uaccess state at function scope and track whether it was
> saved. Restore it at the fault label only when needed. Paths that did not
> enable user access continue to skip the restore.
>
> Fixes: 274e91b81ed2 ("ARM: alignment: fix alignment handling for uaccess changes")
> Cc: stable@vger.kernel.org
> Assisted-by: Codex:gpt-5.6-sol
> Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
Wow nice patch!
Reviewed-by: Linus Walleij <linusw@kernel.org>
Please put this patch into Russell's patch tracker so he can
apply it!
https://www.arm.linux.org.uk/developer/patches/
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-12 7:29 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-11 16:45 [PATCH RESEND] ARM: alignment: restore uaccess state on emulation fault Karl Mehltretter
2026-08-12 7:28 ` Linus Walleij
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®