Re: [RFC PATCH v2 13/27] mm: Handle shadow stack page fault
From: Dave Hansen <dave.hansen@linux.intel.com>
Date: 2018-07-10 23:06:47
Also in:
linux-arch, linux-doc, linux-mm, lkml
On 07/10/2018 03:26 PM, Yu-cheng Yu wrote:
quoted hunk ↗ jump to hunk
+ if (is_shstk_mapping(vma->vm_flags)) + entry = pte_mkdirty_shstk(entry); + else + entry = pte_mkdirty(entry); + + entry = maybe_mkwrite(entry, vma); if (ptep_set_access_flags(vma, vmf->address, vmf->pte, entry, 1)) update_mmu_cache(vma, vmf->address, vmf->pte); pte_unmap_unlock(vmf->pte, vmf->ptl);@@ -2526,7 +2532,11 @@ static int wp_page_copy(struct vm_fault *vmf) } flush_cache_page(vma, vmf->address, pte_pfn(vmf->orig_pte)); entry = mk_pte(new_page, vma->vm_page_prot); - entry = maybe_mkwrite(pte_mkdirty(entry), vma); + if (is_shstk_mapping(vma->vm_flags)) + entry = pte_mkdirty_shstk(entry); + else + entry = pte_mkdirty(entry); + entry = maybe_mkwrite(entry, vma);
Do we want to lift this hunk of code and put it elsewhere? Maybe:
entry = pte_set_vma_features(entry, vma);
and then:
pte_t pte_set_vma_features(pte_t entry, struct vm_area_struct)
{
/*
* Shadow stack PTEs are always dirty and always
* writable. They have a different encoding for
* this than normal PTEs, though.
*/
if (is_shstk_mapping(vma->vm_flags))
entry = pte_mkdirty_shstk(entry);
else
entry = pte_mkdirty(entry);
entry = maybe_mkwrite(entry, vma);
return entry;
}
quoted hunk ↗ jump to hunk
/* * Clear the pte entry and flush it first, before updating the * pte with the new entry. This will avoid a race condition@@ -3201,6 +3211,14 @@ static int do_anonymous_page(struct vm_fault *vmf) mem_cgroup_commit_charge(page, memcg, false, false); lru_cache_add_active_or_unevictable(page, vma); setpte: + /* + * If this is within a shadow stack mapping, mark + * the PTE dirty. We don't use pte_mkdirty(), + * because the PTE must have _PAGE_DIRTY_HW set. + */ + if (is_shstk_mapping(vma->vm_flags)) + entry = pte_mkdirty_shstk(entry); + set_pte_at(vma->vm_mm, vmf->address, vmf->pte, entry);
I'm not sure this is the right spot to do this. The other code does pte_mkdirty_shstk() near where we do the pte_mkwrite(). Why not here? I think you might have picked this because it's a common path used by both allocated pages and zero pages. But, we can't have the zero pages in shadow stack areas since they can't be read-only. I think you need to move this up. Can you even consolidate it with the other two pte_mkdirt_shstk() call sites?
quoted hunk ↗ jump to hunk
/* No need to invalidate - it was non-present before */@@ -3983,6 +4001,14 @@ static int handle_pte_fault(struct vm_fault *vmf) entry = vmf->orig_pte; if (unlikely(!pte_same(*vmf->pte, entry))) goto unlock; + + /* + * Shadow stack PTEs are copy-on-access, so do_wp_page() + * handling on them no matter if we have write fault or not. + */
I'd say this differently: Shadow stack PTEs can not be read-only and because of that can not have traditional copy-on-write semantics. This essentially performs a copy-on-write operation, but on *any* access, not just actual writes.