• Alexey Kardashevskiy's avatar
    powerpc/kuap: Restore AMR after replaying soft interrupts · 60a707d0
    Alexey Kardashevskiy authored
    Since de78a9c4 ("powerpc: Add a framework for Kernel Userspace
    Access Protection"), user access helpers call user_{read|write}_access_{begin|end}
    when user space access is allowed.
    
    Commit 890274c2 ("powerpc/64s: Implement KUAP for Radix MMU") made
    the mentioned helpers program a AMR special register to allow such
    access for a short period of time, most of the time AMR is expected to
    block user memory access by the kernel.
    
    Since the code accesses the user space memory, unsafe_get_user() calls
    might_fault() which calls arch_local_irq_restore() if either
    CONFIG_PROVE_LOCKING or CONFIG_DEBUG_ATOMIC_SLEEP is enabled.
    arch_local_irq_restore() then attempts to replay pending soft
    interrupts as KUAP regions have hardware interrupts enabled.
    
    If a pending interrupt happens to do user access (performance
    interrupts do that), it enables access for a short period of time so
    after returning from the replay, the user access state remains blocked
    and if a user page fault happens - "Bug: Read fault blocked by AMR!"
    appears and SIGSEGV is sent.
    
    An example trace:
      Bug: Read fault blocked by AMR!
      WARNING: CPU: 0 PID: 1603 at /home/aik/p/kernel/arch/powerpc/include/asm/book3s/64/kup-radix.h:145
      CPU: 0 PID: 1603 Comm: amr Not tainted 5.10.0-rc6_v5.10-rc6_a+fstn1 #24
      NIP:  c00000000009ece8 LR: c00000000009ece4 CTR: 0000000000000000
      REGS: c00000000dc63560 TRAP: 0700   Not tainted  (5.10.0-rc6_v5.10-rc6_a+fstn1)
      MSR:  8000000000021033 <SF,ME,IR,DR,RI,LE>  CR: 28002888  XER: 20040000
      CFAR: c0000000001fa928 IRQMASK: 1
      GPR00: c00000000009ece4 c00000000dc637f0 c000000002397600 000000000000001f
      GPR04: c0000000020eb318 0000000000000000 c00000000dc63494 0000000000000027
      GPR08: c00000007fe4de68 c00000000dfe9180 0000000000000000 0000000000000001
      GPR12: 0000000000002000 c0000000030a0000 0000000000000000 0000000000000000
      GPR16: 0000000000000000 0000000000000000 0000000000000000 bfffffffffffffff
      GPR20: 0000000000000000 c0000000134a4020 c0000000019c2218 0000000000000fe0
      GPR24: 0000000000000000 0000000000000000 c00000000d106200 0000000040000000
      GPR28: 0000000000000000 0000000000000300 c00000000dc63910 c000000001946730
      NIP __do_page_fault+0xb38/0xde0
      LR  __do_page_fault+0xb34/0xde0
      Call Trace:
        __do_page_fault+0xb34/0xde0 (unreliable)
        handle_page_fault+0x10/0x2c
      --- interrupt: 300 at strncpy_from_user+0x290/0x440
          LR = strncpy_from_user+0x284/0x440
        strncpy_from_user+0x2f0/0x440 (unreliable)
        getname_flags+0x88/0x2c0
        do_sys_openat2+0x2d4/0x5f0
        do_sys_open+0xcc/0x140
        system_call_exception+0x160/0x240
        system_call_common+0xf0/0x27c
    
    To fix it save/restore the AMR when replaying interrupts, and also
    add a check if AMR was not blocked prior to replaying interrupts.
    
    Originally found by syzkaller.
    
    Fixes: 890274c2 ("powerpc/64s: Implement KUAP for Radix MMU")
    Signed-off-by: default avatarAlexey Kardashevskiy <aik@ozlabs.ru>
    Reviewed-by: default avatarNicholas Piggin <npiggin@gmail.com>
    [mpe: Use normal commit citation format and add full oops log to
          change log, move kuap_check_amr() into the restore routine to
          avoid warnings about unreconciled IRQ state]
    Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
    Link: https://lore.kernel.org/r/20210202091541.36499-1-aik@ozlabs.ru
    60a707d0
irq.c 21 KB