x86/PV: further harden guest memory accesses against speculative abuse

The original implementation has two issues: For one it doesn't preserve
non-canonical-ness of inputs in the range 0x8000000000000000 through
0x80007fffffffffff. Bogus guest pointers in that range would not cause a
(#GP) fault upon access, when they should.

And then there is an AMD-specific aspect, where only the low 48 bits of
an address are used for speculative execution; the architecturally
mandated #GP for non-canonical addresses would be raised at a later
execution stage. Therefore to prevent Xen controlled data to make it
into any of the caches in a guest controllable manner, we need to
additionally ensure that for non-canonical inputs bit 47 would be clear.

See the code comment for how addressing both is being achieved.

Fixes: 4dc1815991 ("x86/PV: harden guest memory accesses against speculative abuse")
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
master commit: 8306d773b0
master date: 2025-01-27 15:23:59 +0100
This commit is contained in:
Jan Beulich committed 2025-02-17 13:21:30 +01:00
1 parent b7b0ce5e11
commit 125270d49d
1 file changed
+27 -6
+27 -6
View File
@@ -1,3 +1,5 @@
#include <asm/page-bits.h>
#ifndef HAVE_AS_CLAC_STAC
.macro clac
.byte 0x0f, 0x01, 0xca
@@ -65,17 +67,36 @@
.macro guest_access_mask_ptr ptr:req, scratch1:req, scratch2:req
#if defined(CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS)
/*
* Here we want
*
* ptr &= ~0ull >> (ptr < HYPERVISOR_VIRT_END);
*
* Here we want to adjust \ptr such that
* - if it's within Xen range, it becomes non-canonical,
* - otherwise if it's (non-)canonical on input, it retains that property,
* - if the result is non-canonical, bit 47 is clear (to avoid
* potentially populating the cache with Xen data on AMD-like hardware),
* but guaranteed without any conditional branches (hence in assembly).
*
* To achieve this we determine which bit to forcibly clear: Either bit 47
* (in case the address is below HYPERVISOR_VIRT_END) or bit 63. Further
* we determine whether for forcably set bit 63: In case we first cleared
* it, we'll merely restore the original address. In case we ended up
* clearing bit 47 (i.e. the address was either non-canonical or within Xen
* range), setting the bit will yield a guaranteed non-canonical address.
* If we didn't clear a bit, we also won't set one: The address was in the
* low half of address space in that case with bit 47 already clear. The
* address can thus be left unchanged, whether canonical or not.
*/
mov $(HYPERVISOR_VIRT_END - 1), \scratch1
mov $~0, \scratch2
mov $(VADDR_BITS - 1), \scratch2
cmp \ptr, \scratch1
/*
* Not needed: The value we have in \scratch1 will be truncated to 6 bits,
* thus yielding the value we need.
mov $63, \scratch1
*/
cmovnb \scratch2, \scratch1
xor \scratch2, \scratch2
btr \scratch1, \ptr
rcr $1, \scratch2
and \scratch2, \ptr
or \scratch2, \ptr
#elif defined(CONFIG_DEBUG) && defined(CONFIG_PV)
xor $~\@, \scratch1
xor $~\@, \scratch2
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.