ARM: alignment: fix alignment handling for uaccess changes
authorRussell King <rmk+kernel@arm.linux.org.uk>
Wed, 23 Sep 2015 10:06:30 +0000 (11:06 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Thu, 24 Sep 2015 10:07:00 +0000 (11:07 +0100)
Jonathan Liu reports that the recent addition of CPU_SW_DOMAIN_PAN
causes wpa_supplicant to die due to the following kernel oops:

Unhandled fault: page domain fault (0x81b) at 0x001017a2
pgd = ee1b8000
[001017a2] *pgd=6ebee831, *pte=6c35475f, *ppte=6c354c7f
Internal error: : 81b [#1] SMP ARM
Modules linked in: rt2800usb rt2x00usb rt2800librt2x00lib crc_ccitt mac80211
CPU: 1 PID: 202 Comm: wpa_supplicant Not tainted 4.3.0-rc2 #1
Hardware name: Allwinner sun7i (A20) Family
task: ec872f80 ti: ee364000 task.ti: ee364000
PC is at do_alignment_ldmstm+0x1d4/0x238
LR is at 0x0
pc : [<c001d1d8>]    lr : [<00000000>]    psr: 600c0113
sp : ee365e18  ip : 00000000  fp : 00000002
r10: 001017a2  r9 : 00000002  r8 : 001017aa
r7 : ee365fb0  r6 : e8820018  r5 : 001017a2  r4 : 00000003
r3 : d49e30e0  r2 : 00000000  r1 : ee365fbc  r0 : 00000000
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none[   34.393106] Control: 10c5387d  Table: 6e1b806a  DAC: 00000051
Process wpa_supplicant (pid: 202, stack limit = 0xee364210)
Stack: (0xee365e18 to 0xee366000)
...
[<c001d1d8>] (do_alignment_ldmstm) from [<c001d510>] (do_alignment+0x1f0/0x904)
[<c001d510>] (do_alignment) from [<c00092a0>] (do_DataAbort+0x38/0xb4)
[<c00092a0>] (do_DataAbort) from [<c0013d7c>] (__dabt_usr+0x3c/0x40)
Exception stack(0xee365fb0 to 0xee365ff8)
5fa0:                                     00000000 56c728c0 001017a2 d49e30e0
5fc0: 775448d2 597d4e74 00200800 7a9e1625 00802001 00000021 b6deec84 00000100
5fe0: 08020200 be9f4f20 0c0b0d0a b6d9b3e0 600c0010 ffffffff
Code: e1a0a005 e1a0000c 1affffe8 e5913000 (e4ea3001)
---[ end trace 0acd3882fcfdf9dd ]---

This is caused by the alignment handler not being fixed up for the
uaccess changes, and userspace issuing an unaligned LDM instruction.
So, fix the problem by adding the necessary fixups.

Reported-by: Jonathan Liu <net147@gmail.com>
Tested-by: Jonathan Liu <net147@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/mm/alignment.c

index 9769f1eefe3bc51b29188576a5e2aacc2f1dcedc..00b7f7de28a182c849249a242fb0ecd2d68b09ca 100644 (file)
@@ -365,15 +365,21 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r
  user:
        if (LDST_L_BIT(instr)) {
                unsigned long val;
+               unsigned int __ua_flags = uaccess_save_and_enable();
+
                get16t_unaligned_check(val, addr);
+               uaccess_restore(__ua_flags);
 
                /* signed half-word? */
                if (instr & 0x40)
                        val = (signed long)((signed short) val);
 
                regs->uregs[rd] = val;
-       } else
+       } else {
+               unsigned int __ua_flags = uaccess_save_and_enable();
                put16t_unaligned_check(regs->uregs[rd], addr);
+               uaccess_restore(__ua_flags);
+       }
 
        return TYPE_LDST;
 
@@ -420,14 +426,21 @@ do_alignment_ldrdstrd(unsigned long addr, unsigned long instr,
 
  user:
        if (load) {
-               unsigned long val;
+               unsigned long val, val2;
+               unsigned int __ua_flags = uaccess_save_and_enable();
+
                get32t_unaligned_check(val, addr);
+               get32t_unaligned_check(val2, addr + 4);
+
+               uaccess_restore(__ua_flags);
+
                regs->uregs[rd] = val;
-               get32t_unaligned_check(val, addr + 4);
-               regs->uregs[rd2] = val;
+               regs->uregs[rd2] = val2;
        } else {
+               unsigned int __ua_flags = uaccess_save_and_enable();
                put32t_unaligned_check(regs->uregs[rd], addr);
                put32t_unaligned_check(regs->uregs[rd2], addr + 4);
+               uaccess_restore(__ua_flags);
        }
 
        return TYPE_LDST;
@@ -458,10 +471,15 @@ do_alignment_ldrstr(unsigned long addr, unsigned long instr, struct pt_regs *reg
  trans:
        if (LDST_L_BIT(instr)) {
                unsigned int val;
+               unsigned int __ua_flags = uaccess_save_and_enable();
                get32t_unaligned_check(val, addr);
+               uaccess_restore(__ua_flags);
                regs->uregs[rd] = val;
-       } else
+       } else {
+               unsigned int __ua_flags = uaccess_save_and_enable();
                put32t_unaligned_check(regs->uregs[rd], addr);
+               uaccess_restore(__ua_flags);
+       }
        return TYPE_LDST;
 
  fault:
@@ -531,6 +549,7 @@ do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *reg
 #endif
 
        if (user_mode(regs)) {
+               unsigned int __ua_flags = uaccess_save_and_enable();
                for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
                     regbits >>= 1, rd += 1)
                        if (regbits & 1) {
@@ -542,6 +561,7 @@ do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *reg
                                        put32t_unaligned_check(regs->uregs[rd], eaddr);
                                eaddr += 4;
                        }
+               uaccess_restore(__ua_flags);
        } else {
                for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
                     regbits >>= 1, rd += 1)