mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
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:
1 parent
0d6968635c
commit
8f2e3d8913
1 file changed
+32
-31
@@ -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;
|
||||
|
||||
/*
|
||||
|
||||