• Rick Edgecombe's avatar
    mm: Add guard pages around a shadow stack. · 0266e7c5
    Rick Edgecombe authored
    The x86 Control-flow Enforcement Technology (CET) feature includes a new
    type of memory called shadow stack. This shadow stack memory has some
    unusual properties, which requires some core mm changes to function
    properly.
    
    The architecture of shadow stack constrains the ability of userspace to
    move the shadow stack pointer (SSP) in order to prevent corrupting or
    switching to other shadow stacks. The RSTORSSP instruction can move the
    SSP to different shadow stacks, but it requires a specially placed token
    in order to do this. However, the architecture does not prevent
    incrementing the stack pointer to wander onto an adjacent shadow stack. To
    prevent this in software, enforce guard pages at the beginning of shadow
    stack VMAs, such that there will always be a gap between adjacent shadow
    stacks.
    
    Make the gap big enough so that no userspace SSP changing operations
    (besides RSTORSSP), can move the SSP from one stack to the next. The
    SSP can be incremented or decremented by CALL, RET  and INCSSP. CALL and
    RET can move the SSP by a maximum of 8 bytes, at which point the shadow
    stack would be accessed.
    
    The INCSSP instruction can also increment the shadow stack pointer. It
    is the shadow stack analog of an instruction like:
    
            addq    $0x80, %rsp
    
    However, there is one important difference between an ADD on %rsp and
    INCSSP. In addition to modifying SSP, INCSSP also reads from the memory
    of the first and last elements that were "popped". It can be thought of
    as acting like this:
    
    READ_ONCE(ssp);       // read+discard top element on stack
    ssp += nr_to_pop * 8; // move the shadow stack
    READ_ONCE(ssp-8);     // read+discard last popped stack element
    
    The maximum distance INCSSP can move the SSP is 2040 bytes, before it
    would read the memory. Therefore, a single page gap will be enough to
    prevent any operation from shifting the SSP to an adjacent stack, since
    it would have to land in the gap at least once, causing a fault.
    
    This could be accomplished by using VM_GROWSDOWN, but this has a
    downside. The behavior would allow shadow stacks to grow, which is
    unneeded and adds a strange difference to how most regular stacks work.
    
    In the maple tree code, there is some logic for retrying the unmapped
    area search if a guard gap is violated. This retry should happen for
    shadow stack guard gap violations as well. This logic currently only
    checks for VM_GROWSDOWN for start gaps. Since shadow stacks also have
    a start gap as well, create an new define VM_STARTGAP_FLAGS to hold
    all the VM flag bits that have start gaps, and make mmap use it.
    Co-developed-by: default avatarYu-cheng Yu <yu-cheng.yu@intel.com>
    Signed-off-by: default avatarYu-cheng Yu <yu-cheng.yu@intel.com>
    Signed-off-by: default avatarRick Edgecombe <rick.p.edgecombe@intel.com>
    Signed-off-by: default avatarDave Hansen <dave.hansen@linux.intel.com>
    Reviewed-by: default avatarBorislav Petkov (AMD) <bp@alien8.de>
    Reviewed-by: default avatarKees Cook <keescook@chromium.org>
    Reviewed-by: default avatarMark Brown <broonie@kernel.org>
    Acked-by: default avatarMike Rapoport (IBM) <rppt@kernel.org>
    Tested-by: default avatarPengfei Xu <pengfei.xu@intel.com>
    Tested-by: default avatarJohn Allen <john.allen@amd.com>
    Tested-by: default avatarKees Cook <keescook@chromium.org>
    Link: https://lore.kernel.org/all/20230613001108.3040476-17-rick.p.edgecombe%40intel.com
    0266e7c5
mmap.c 103 KB