x86/pagewalk: Fix determination of Protection Key access rights

* When fabricating gl1e's from superpages, propagate the protection key as
   well, so the protection key logic sees the real key as opposed to 0.

 * Experimentally, the protection key checks are performed ahead of the other
   access rights.  In particular, accesses which fail both protection key and
   regular permission checks yield PFEC_prot_key in the resulting pagefault.

 * Protection keys apply to all data accesses to user-mode addresses,
   including accesses from supervisor code.  PKRU WD applies to any data
   write, not just to mapping which are writable.  However, a supervisor
   access without CR0.WP bypasses any protection from protection keys.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Tim Deegan <tim@xen.org>
Release-acked-by: Julien Grall <julien.grall@arm.com>
This commit is contained in:
Andrew Cooper committed 2017-05-19 15:41:45 +01:00
1 parent 0d6968635c
commit 8f2e3d8913
1 file changed
+32 -31
+32 -31
View File
@@ -197,12 +197,12 @@ guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m,
int flags = (_PAGE_PRESENT|_PAGE_USER|_PAGE_RW|
_PAGE_ACCESSED|_PAGE_DIRTY);
/*
* Import cache-control bits. Note that _PAGE_PAT is actually
* _PAGE_PSE, and it is always set. We will clear it in case
* _PAGE_PSE_PAT (bit 12, i.e. first bit of gfn) is clear.
* Import protection key and cache-control bits. Note that _PAGE_PAT
* is actually _PAGE_PSE, and it is always set. We will clear it in
* case _PAGE_PSE_PAT (bit 12, i.e. first bit of gfn) is clear.
*/
flags |= (guest_l3e_get_flags(gw->l3e)
& (_PAGE_PAT|_PAGE_PWT|_PAGE_PCD));
& (_PAGE_PKEY_BITS|_PAGE_PAT|_PAGE_PWT|_PAGE_PCD));
if ( !(gfn_x(start) & 1) )
/* _PAGE_PSE_PAT not set: remove _PAGE_PAT from flags. */
flags &= ~_PAGE_PAT;
@@ -302,12 +302,12 @@ guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m,
int flags = (_PAGE_PRESENT|_PAGE_USER|_PAGE_RW|
_PAGE_ACCESSED|_PAGE_DIRTY);
/*
* Import cache-control bits. Note that _PAGE_PAT is actually
* _PAGE_PSE, and it is always set. We will clear it in case
* _PAGE_PSE_PAT (bit 12, i.e. first bit of gfn) is clear.
* Import protection key and cache-control bits. Note that _PAGE_PAT
* is actually _PAGE_PSE, and it is always set. We will clear it in
* case _PAGE_PSE_PAT (bit 12, i.e. first bit of gfn) is clear.
*/
flags |= (guest_l2e_get_flags(gw->l2e)
& (_PAGE_PAT|_PAGE_PWT|_PAGE_PCD));
& (_PAGE_PKEY_BITS|_PAGE_PAT|_PAGE_PWT|_PAGE_PCD));
if ( !(gfn_x(start) & 1) )
/* _PAGE_PSE_PAT not set: remove _PAGE_PAT from flags. */
flags &= ~_PAGE_PAT;
@@ -365,6 +365,30 @@ guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m,
*/
ar = (ar_and & AR_ACCUM_AND) | (ar_or & AR_ACCUM_OR);
#if GUEST_PAGING_LEVELS >= 4 /* 64-bit only... */
/*
* If all access checks are thus far ok, check Protection Key for 64bit
* data accesses to user mappings.
*
* N.B. In the case that the walk ended with a superpage, the fabricated
* gw->l1e contains the appropriate leaf pkey.
*/
if ( (ar & _PAGE_USER) && !(walk & PFEC_insn_fetch) &&
guest_pku_enabled(v) )
{
unsigned int pkey = guest_l1e_get_pkey(gw->l1e);
unsigned int pkru = read_pkru();
if ( read_pkru_ad(pkru, pkey) ||
((walk & PFEC_write_access) && read_pkru_wd(pkru, pkey) &&
((walk & PFEC_user_mode) || guest_wp_enabled(v))) )
{
gw->pfec |= PFEC_prot_key;
goto out;
}
}
#endif
if ( (walk & PFEC_insn_fetch) && (ar & _PAGE_NX_BIT) )
/* Requested an instruction fetch and found NX? Fail. */
goto out;
@@ -400,29 +424,6 @@ guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m,
goto out;
}
#if GUEST_PAGING_LEVELS >= 4 /* 64-bit only... */
/*
* If all access checks are thusfar ok, check Protection Key for 64bit
* user data accesses.
*
* N.B. In the case that the walk ended with a superpage, the fabricated
* gw->l1e contains the appropriate leaf pkey.
*/
if ( (walk & PFEC_user_mode) && !(walk & PFEC_insn_fetch) &&
guest_pku_enabled(v) )
{
unsigned int pkey = guest_l1e_get_pkey(gw->l1e);
unsigned int pkru = read_pkru();
if ( read_pkru_ad(pkru, pkey) ||
((ar & PFEC_write_access) && read_pkru_wd(pkru, pkey)) )
{
gw->pfec |= PFEC_prot_key;
goto out;
}
}
#endif
walk_ok = true;
/*
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.