lkml.org 
[lkml]   [2019]   [Oct]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.4 16/36] ARM: 8898/1: mm: Dont treat faults reported from cache maintenance as writes
    Date
    From: Will Deacon <will@kernel.org>

    [ Upstream commit 834020366da9ab3fb87d1eb9a3160eb22dbed63a ]

    Translation faults arising from cache maintenance instructions are
    rather unhelpfully reported with an FSR value where the WnR field is set
    to 1, indicating that the faulting access was a write. Since cache
    maintenance instructions on 32-bit ARM do not require any particular
    permissions, this can cause our private 'cacheflush' system call to fail
    spuriously if a translation fault is generated due to page aging when
    targetting a read-only VMA.

    In this situation, we will return -EFAULT to userspace, although this is
    unfortunately suppressed by the popular '__builtin___clear_cache()'
    intrinsic provided by GCC, which returns void.

    Although it's tempting to write this off as a userspace issue, we can
    actually do a little bit better on CPUs that support LPAE, even if the
    short-descriptor format is in use. On these CPUs, cache maintenance
    faults additionally set the CM field in the FSR, which we can use to
    suppress the write permission checks in the page fault handler and
    succeed in performing cache maintenance to read-only areas even in the
    presence of a translation fault.

    Reported-by: Orion Hodson <oth@google.com>
    Signed-off-by: Will Deacon <will@kernel.org>
    Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    arch/arm/mm/fault.c | 4 ++--
    arch/arm/mm/fault.h | 1 +
    2 files changed, 3 insertions(+), 2 deletions(-)

    diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
    index 0d20cd5940171..702a5542b11a8 100644
    --- a/arch/arm/mm/fault.c
    +++ b/arch/arm/mm/fault.c
    @@ -211,7 +211,7 @@ static inline bool access_error(unsigned int fsr, struct vm_area_struct *vma)
    {
    unsigned int mask = VM_READ | VM_WRITE | VM_EXEC;

    - if (fsr & FSR_WRITE)
    + if ((fsr & FSR_WRITE) && !(fsr & FSR_CM))
    mask = VM_WRITE;
    if (fsr & FSR_LNX_PF)
    mask = VM_EXEC;
    @@ -281,7 +281,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)

    if (user_mode(regs))
    flags |= FAULT_FLAG_USER;
    - if (fsr & FSR_WRITE)
    + if ((fsr & FSR_WRITE) && !(fsr & FSR_CM))
    flags |= FAULT_FLAG_WRITE;

    /*
    diff --git a/arch/arm/mm/fault.h b/arch/arm/mm/fault.h
    index 78830657cab3a..b014e57248044 100644
    --- a/arch/arm/mm/fault.h
    +++ b/arch/arm/mm/fault.h
    @@ -5,6 +5,7 @@
    * Fault status register encodings. We steal bit 31 for our own purposes.
    */
    #define FSR_LNX_PF (1 << 31)
    +#define FSR_CM (1 << 13)
    #define FSR_WRITE (1 << 11)
    #define FSR_FS4 (1 << 10)
    #define FSR_FS3_0 (15)
    --
    2.20.1


    \
     
     \ /
      Last update: 2019-10-06 19:20    [W:4.587 / U:0.144 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site