[ adding Hugh ]

On Thu, 4 Jan 2018, Dave Hansen wrote:

> > BTW, we have just reported a bug caused by kaiser[1], which looks like
> > caused by SMEP. Could you please help to have a look?
> > 
> > [1] https://lkml.org/lkml/2018/1/5/3
> 
> Please report that to your kernel vendor.  Your EFI page tables have the
> NX bit set on the low addresses.  There have been a bunch of iterations
> of this, but you need to make sure that the EFI kernel mappings don't
> get _PAGE_NX set on them.  Look at what __pti_set_user_pgd() does in
> mainline.

Unfortunately this is more complicated.

The thing is -- efi=old_memmap is broken even upstream. We will probably 
not receive too many reports about this against upstream PTI, as most of 
the machines are using classic high-mapping of EFI regions; but older 
kernels force on certain machines stil old_memmap (or it can be specified 
manually on kernel cmdline), where EFI has all its mapping in the 
userspace range.

And that explodes, as those get marked NX in the kernel pagetables.

I've spent most of today tracking this down (the legacy EFI mmap is 
horrid); the patch below is confirmed to fix it both on current upstream 
kernel, as well as on original-KAISER based kernels (Hugh's backport) in 
cases old_memmap is used by EFI.

I am not super happy about this, but I din't really want to extend the 
_set_pgd() code to always figure out whether it's dealing wih low EFI 
mapping or not, as that would be way too much overhead just for this 
one-off call during boot.



From: Jiri Kosina <jkos...@suse.cz>
Subject: [PATCH] PTI: unbreak EFI old_memmap

old_memmap's efi_call_phys_prolog() calls set_pgd() with swapper PGD that 
has PAGE_USER set, which makes PTI set NX on it, and therefore EFI can't 
execute it's code.

Fix that by forcefully clearing _PAGE_NX from the PGD (this can't be done
by the pgprot API).

_PAGE_NX will be automatically reintroduced in efi_call_phys_epilog(), as 
_set_pgd() will again notice that this is _PAGE_USER, and set _PAGE_NX on 
it.

Signed-off-by: Jiri Kosina <jkos...@suse.cz>
---
 arch/x86/platform/efi/efi_64.c |    6 ++++++
 1 file changed, 6 insertions(+)

--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -95,6 +95,12 @@ pgd_t * __init efi_call_phys_prolog(void
                save_pgd[pgd] = *pgd_offset_k(pgd * PGDIR_SIZE);
                vaddress = (unsigned long)__va(pgd * PGDIR_SIZE);
                set_pgd(pgd_offset_k(pgd * PGDIR_SIZE), 
*pgd_offset_k(vaddress));
+               /*
+                * pgprot API doesn't clear it for PGD
+                *
+                * Will be brought back automatically in _epilog()
+                */
+               pgd_offset_k(pgd * PGDIR_SIZE)->pgd &= ~_PAGE_NX;
        }
        __flush_tlb_all();
 

-- 
Jiri Kosina
SUSE Labs

Reply via email to