• Rafael J. Wysocki's avatar
    x86/power/64: Fix kernel text mapping corruption during image restoration · 65c0554b
    Rafael J. Wysocki authored
    Logan Gunthorpe reports that hibernation stopped working reliably for
    him after commit ab76f7b4 (x86/mm: Set NX on gap between __ex_table
    and rodata).
    
    That turns out to be a consequence of a long-standing issue with the
    64-bit image restoration code on x86, which is that the temporary
    page tables set up by it to avoid page tables corruption when the
    last bits of the image kernel's memory contents are copied into
    their original page frames re-use the boot kernel's text mapping,
    but that mapping may very well get corrupted just like any other
    part of the page tables.  Of course, if that happens, the final
    jump to the image kernel's entry point will go to nowhere.
    
    The exact reason why commit ab76f7b4 matters here is that it
    sometimes causes a PMD of a large page to be split into PTEs
    that are allocated dynamically and get corrupted during image
    restoration as described above.
    
    To fix that issue note that the code copying the last bits of the
    image kernel's memory contents to the page frames occupied by them
    previoulsy doesn't use the kernel text mapping, because it runs from
    a special page covered by the identity mapping set up for that code
    from scratch.  Hence, the kernel text mapping is only needed before
    that code starts to run and then it will only be used just for the
    final jump to the image kernel's entry point.
    
    Accordingly, the temporary page tables set up in swsusp_arch_resume()
    on x86-64 need to contain the kernel text mapping too.  That mapping
    is only going to be used for the final jump to the image kernel, so
    it only needs to cover the image kernel's entry point, because the
    first thing the image kernel does after getting control back is to
    switch over to its own original page tables.  Moreover, the virtual
    address of the image kernel's entry point in that mapping has to be
    the same as the one mapped by the image kernel's page tables.
    
    With that in mind, modify the x86-64's arch_hibernation_header_save()
    and arch_hibernation_header_restore() routines to pass the physical
    address of the image kernel's entry point (in addition to its virtual
    address) to the boot kernel (a small piece of assembly code involved
    in passing the entry point's virtual address to the image kernel is
    not necessary any more after that, so drop it).  Update RESTORE_MAGIC
    too to reflect the image header format change.
    
    Next, in set_up_temporary_mappings(), use the physical and virtual
    addresses of the image kernel's entry point passed in the image
    header to set up a minimum kernel text mapping (using memory pages
    that won't be overwritten by the image kernel's memory contents) that
    will map those addresses to each other as appropriate.
    
    This makes the concern about the possible corruption of the original
    boot kernel text mapping go away and if the the minimum kernel text
    mapping used for the final jump marks the image kernel's entry point
    memory as executable, the jump to it is guaraneed to succeed.
    
    Fixes: ab76f7b4 (x86/mm: Set NX on gap between __ex_table and rodata)
    Link: http://marc.info/?l=linux-pm&m=146372852823760&w=2Reported-by: default avatarLogan Gunthorpe <logang@deltatee.com>
    Reported-and-tested-by: default avatarBorislav Petkov <bp@suse.de>
    Tested-by: default avatarKees Cook <keescook@chromium.org>
    Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
    65c0554b
hibernate_64.c 5.61 KB