* [PATCH v4 0/7] KVM: x86: Add LASS virtualization support
@ 2026-08-06 1:15 Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 1/7] KVM: x86: Add an emulator flag to differentiate branch targets from fetches Sohil Mehta
` (6 more replies)
0 siblings, 7 replies; 10+ messages in thread
From: Sohil Mehta @ 2026-08-06 1:15 UTC (permalink / raw)
To: kvm, x86
Cc: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H . Peter Anvin, Shuah Khan,
Binbin Wu, Peter Zijlstra, Chang S . Bae, Kai Huang, Fuad Tabba,
Chao Gao, Yosry Ahmed, Claudio Imbrenda, David Matlack,
Bala-Vignesh-Reddy, Kishen Maloor, Rick Edgecombe, Sohil Mehta,
linux-kernel, linux-kselftest
Linear Address Space Separation (LASS) is a security feature that blocks
accesses across the user/kernel boundary based on bit 63 of the linear
address alone, before any page walk is performed. Host support for LASS
has been merged [1][2]. This series adds the KVM virtualization support.
Patches
-------
The previous version of the LASS KVM series (v3) was posted as part of
the combined LAM and LASS KVM series. The LAM patches from that series,
along with the emulator flag cleanups that LASS depended on, were merged
separately. The remaining LASS patches went unposted for some time while
the host support was being merged.
I have refreshed the patches and rebased them onto the latest
kvm-x86/next branch. This iteration is marked as v4 to keep a sense of
continuity.
v3: https://lore.kernel.org/lkml/20230913124227.12574-1-binbin.wu@linux.intel.com/
Changes in v4
-------------
- Rebased the patches onto kvm-x86/next (7.2-rc2 based)
- Reorganized the patches and reworded the changelogs
- Switched to gva_t for the LASS address parameter throughout
- Advertised LASS with X86_64_F() so it isn't exposed on 32-bit
- Exempted branch targets from LAM untagging
- Added emulator TSS I/O bitmap cleanup
- Added basic KVM and x86 selftests
Background
----------
The host support series [3] covers the motivation, base enforcement
mechanism, kernel toggling of RFLAGS and CR4.LASS, and the userspace
exception notifications. Here's a brief summary of the SDM bits [4] that
affects KVM support.
When LASS is enabled, the CPU applies a violation check using bit 63 to
every access to a linear address prior to page walks. A user-mode access
to a supervisor address, or a supervisor-mode access to a user address,
typically raises #GP (or #SS in rare cases) instead of a #PF that
SMAP/SMEP alone would produce. LASS takes effect only in IA-32e mode.
Enforcement for supervisor-mode data accesses additionally requires SMAP
to be enabled, and is suppressed for explicit accesses when RFLAGS.AC=1.
Linear addresses used for TLB invalidation (INVLPG, INVPCID, INVVPID)
are not subject to LASS. Unlike canonicality checks, LASS only applies
to code fetches and not branch targets.
Note, LASS is now part of the SDM instead of the ISE. There are minor
changes to the wording but nothing substantial. It also includes a
clarification that the relative ordering of LASS and canonicality checks
is not defined and cannot be determined by software.
KVM support
-----------
KVM must apply the same LASS violation checks as hardware during
instruction emulation so that emulated accesses behave the same way.
Patch 1-4: Enhance the emulator to handle LASS violation checks.
Patch 5: Guest CPUID and CR4 handling. Expose LASS to userspace.
Patch 6-7: Basic KVM and x86 selftest for LASS.
Though functional, the tests in patches 6 and 7 are fairly limited and
mainly for reference and discussion.
Testing
-------
1. Basic enumeration and enabling in guest and nested environment.
2. LASS enforcement tests (userspace + LKDTM + test kernel module)
- Userspace access to kernel address (read, write, instr fetch)
- Kernel access to user address (read, write, instr fetch)
- RFLAGS.AC=1 suppression (read, write)
- FEP tests for the above cases
KVM selftests and kvm-unit-tests run guest code in the lower half at
CPL0, so enabling CR4.LASS makes the next instruction fetch a violation
and triple-faults the guest. We are evaluating if the infrastructure can
be enhanced to test LASS enforcement.
Links
-----
[1]: https://lore.kernel.org/lkml/20251201231537.736899-1-dave.hansen@linux.intel.com/
[2]: https://lore.kernel.org/lkml/20260413154235.1543087-1-dave.hansen@linux.intel.com/
[3]: https://lore.kernel.org/all/20251118182911.2983253-1-sohil.mehta@intel.com/
[4]: "Linear-Address Pre-Processing", Intel SDM (June 2026), Vol 3, Chapter 4.
Previous versions
v3: https://lore.kernel.org/lkml/20230913124227.12574-1-binbin.wu@linux.intel.com/
v2: https://lore.kernel.org/all/20230718131844.5706-1-guang.zeng@intel.com/
v1: https://lore.kernel.org/all/20230601142309.6307-1-guang.zeng@intel.com/
v0: https://lore.kernel.org/lkml/20230420133724.11398-1-guang.zeng@intel.com/
Binbin Wu (1):
KVM: x86: Add an emulator flag to differentiate branch targets from
fetches
Sohil Mehta (3):
KVM: x86: Use linear_read_system() to read the TSS I/O bitmap
KVM: selftests: Add coverage for LASS CPUID and CR4 handling
selftests/x86: Add a userspace test for LASS enforcement
Zeng Guang (3):
KVM: x86: Add LASS violation checks during instruction emulation
KVM: VMX: Implement LASS violation check
KVM: x86: Virtualize LASS and advertise support to userspace
arch/x86/include/asm/kvm-x86-ops.h | 1 +
arch/x86/include/asm/kvm_host.h | 2 +
arch/x86/kvm/cpuid.c | 1 +
arch/x86/kvm/emulate.c | 28 ++-
arch/x86/kvm/kvm_emulate.h | 4 +-
arch/x86/kvm/regs.h | 4 +-
arch/x86/kvm/vmx/main.c | 1 +
arch/x86/kvm/vmx/nested.c | 11 +-
arch/x86/kvm/vmx/sgx.c | 3 +-
arch/x86/kvm/vmx/vmx.c | 51 ++++-
arch/x86/kvm/vmx/vmx.h | 3 +
arch/x86/kvm/x86.c | 9 +-
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/include/x86/processor.h | 2 +
tools/testing/selftests/kvm/x86/lass_test.c | 56 +++++
.../selftests/kvm/x86/set_sregs_test.c | 3 +
tools/testing/selftests/x86/Makefile | 3 +-
tools/testing/selftests/x86/lass.c | 196 ++++++++++++++++++
18 files changed, 363 insertions(+), 16 deletions(-)
create mode 100644 tools/testing/selftests/kvm/x86/lass_test.c
create mode 100644 tools/testing/selftests/x86/lass.c
base-commit: 2dfab80a305700a45bd947350dae253ba4e30c41
--
2.43.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v4 1/7] KVM: x86: Add an emulator flag to differentiate branch targets from fetches
2026-08-06 1:15 [PATCH v4 0/7] KVM: x86: Add LASS virtualization support Sohil Mehta
@ 2026-08-06 1:15 ` Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 2/7] KVM: x86: Use linear_read_system() to read the TSS I/O bitmap Sohil Mehta
` (5 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Sohil Mehta @ 2026-08-06 1:15 UTC (permalink / raw)
To: kvm, x86
Cc: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H . Peter Anvin, Shuah Khan,
Binbin Wu, Peter Zijlstra, Chang S . Bae, Kai Huang, Fuad Tabba,
Chao Gao, Yosry Ahmed, Claudio Imbrenda, David Matlack,
Bala-Vignesh-Reddy, Kishen Maloor, Rick Edgecombe, Sohil Mehta,
linux-kernel, linux-kselftest
From: Binbin Wu <binbin.wu@linux.intel.com>
Add a new emulator flag, X86EMUL_F_BRANCH, and use it instead of
X86EMUL_F_FETCH in assign_eip() to distinguish between instruction fetch
and branch target computation for features that handle them differently.
For example, Linear Address Space Separation (LASS) applies to code
fetches but not branch target calculations. A LASS violation occurs only
when the branch target is used to fetch an instruction.
X86EMUL_F_BRANCH is still a code access, so add it to the code segment
readability check in __linearize() along with the Linear Address Masking
(LAM) untagging exemption in vmx_get_untagged_addr().
For now, X86EMUL_F_BRANCH and X86EMUL_F_FETCH are identical as far as
KVM is concerned. No functional change intended.
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v4:
- Added LAM untagging exemption
- Reworded the commit message
---
arch/x86/kvm/emulate.c | 5 +++--
arch/x86/kvm/kvm_emulate.h | 1 +
arch/x86/kvm/vmx/vmx.c | 3 ++-
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 8071b372d233..8ff28643b2e3 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -672,7 +672,8 @@ static __always_inline int __linearize(struct x86_emulate_ctxt *ctxt,
(flags & X86EMUL_F_WRITE))
goto bad;
/* unreadable code segment */
- if (!(flags & X86EMUL_F_FETCH) && (desc.type & 8) && !(desc.type & 2))
+ if (!(flags & (X86EMUL_F_FETCH | X86EMUL_F_BRANCH)) &&
+ (desc.type & 8) && !(desc.type & 2))
goto bad;
lim = desc_limit_scaled(&desc);
if (!(desc.type & 8) && (desc.type & 4)) {
@@ -723,7 +724,7 @@ static inline int assign_eip(struct x86_emulate_ctxt *ctxt, ulong dst)
if (ctxt->op_bytes != sizeof(unsigned long))
addr.ea = dst & ((1UL << (ctxt->op_bytes << 3)) - 1);
rc = __linearize(ctxt, addr, &max_size, 1, ctxt->mode, &linear,
- X86EMUL_F_FETCH);
+ X86EMUL_F_BRANCH);
if (rc == X86EMUL_CONTINUE)
ctxt->_eip = addr.ea;
return rc;
diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
index 3e375af15c03..97421b8dde13 100644
--- a/arch/x86/kvm/kvm_emulate.h
+++ b/arch/x86/kvm/kvm_emulate.h
@@ -105,6 +105,7 @@ struct x86_instruction_info {
#define X86EMUL_F_INVLPG BIT(3)
#define X86EMUL_F_MSR BIT(4)
#define X86EMUL_F_DT_LOAD BIT(5)
+#define X86EMUL_F_BRANCH BIT(6)
struct x86_emulate_ops {
void (*vm_bugged)(struct x86_emulate_ctxt *ctxt);
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index e3bfe6aca1a0..973f7e95be65 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -8571,7 +8571,8 @@ gva_t vmx_get_untagged_addr(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags
int lam_bit;
unsigned long cr3_bits;
- if (flags & (X86EMUL_F_FETCH | X86EMUL_F_IMPLICIT | X86EMUL_F_INVLPG))
+ if (flags & (X86EMUL_F_FETCH | X86EMUL_F_BRANCH | X86EMUL_F_IMPLICIT |
+ X86EMUL_F_INVLPG))
return gva;
if (!is_64_bit_mode(vcpu))
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v4 2/7] KVM: x86: Use linear_read_system() to read the TSS I/O bitmap
2026-08-06 1:15 [PATCH v4 0/7] KVM: x86: Add LASS virtualization support Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 1/7] KVM: x86: Add an emulator flag to differentiate branch targets from fetches Sohil Mehta
@ 2026-08-06 1:15 ` Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 3/7] KVM: x86: Add LASS violation checks during instruction emulation Sohil Mehta
` (4 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Sohil Mehta @ 2026-08-06 1:15 UTC (permalink / raw)
To: kvm, x86
Cc: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H . Peter Anvin, Shuah Khan,
Binbin Wu, Peter Zijlstra, Chang S . Bae, Kai Huang, Fuad Tabba,
Chao Gao, Yosry Ahmed, Claudio Imbrenda, David Matlack,
Bala-Vignesh-Reddy, Kishen Maloor, Rick Edgecombe, Sohil Mehta,
linux-kernel, linux-kselftest
TSS I/O permission bitmap reads are implicit supervisor accesses which
are subject to Linear Address Space Separation (LASS) enforcement.
Though highly unlikely, if a guest configures a TSS base in the
user half, hardware would raise a #GP on access when LASS is enabled.
Currently, the emulator reads the I/O permission bitmap from the TSS by
calling read_std() directly which is inconsistent with other implicit
accesses in the emulator such as IDT reads, GDT/LDT reads and TSS reads
during task switch.
An upcoming change will add a check to linear_read_system() to catch
LASS violations. For consistency as well as to keep LASS enforcement
centralized, switch both I/O bitmap reads to linear_read_system().
Note, emulator_io_port_access_allowed() doesn't propagate faults, so
even though linear_read_system() will set the exception details they
will be ignored.
While at it, fix an off-by-one in the I/O bitmap bounds check to account
for the 2-byte read and ensure both bytes are within the TSS limit. The
SDM mandates a trailing 0xFF byte after the bitmap so any out-of-bounds
access would be all 1s (denying access). Make the change primarily to
ensure hardware fidelity. A correctly configured OS will not run into
this issue.
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v4:
- New patch
There could be a pre-existing issue here. It is unlikely that any OS
demand-pages the I/O bitmap portion of the TSS. But if it does, the #PF
details could get lost and the guest would get a #GP instead of a
restartable #PF. Propagating the #PF to the callers is a larger change
that is beyond the scope of this series.
---
arch/x86/kvm/emulate.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 8ff28643b2e3..7f04544cfee5 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -2573,12 +2573,12 @@ static bool emulator_io_port_access_allowed(struct x86_emulate_ctxt *ctxt,
#ifdef CONFIG_X86_64
base |= ((u64)base3) << 32;
#endif
- r = ops->read_std(ctxt, base + 102, &io_bitmap_ptr, 2, NULL, true);
+ r = linear_read_system(ctxt, base + 102, &io_bitmap_ptr, 2);
if (r != X86EMUL_CONTINUE)
return false;
- if (io_bitmap_ptr + port/8 > desc_limit_scaled(&tr_seg))
+ if (io_bitmap_ptr + port/8 + 1 > desc_limit_scaled(&tr_seg))
return false;
- r = ops->read_std(ctxt, base + io_bitmap_ptr + port/8, &perm, 2, NULL, true);
+ r = linear_read_system(ctxt, base + io_bitmap_ptr + port/8, &perm, 2);
if (r != X86EMUL_CONTINUE)
return false;
if ((perm >> bit_idx) & mask)
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v4 3/7] KVM: x86: Add LASS violation checks during instruction emulation
2026-08-06 1:15 [PATCH v4 0/7] KVM: x86: Add LASS virtualization support Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 1/7] KVM: x86: Add an emulator flag to differentiate branch targets from fetches Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 2/7] KVM: x86: Use linear_read_system() to read the TSS I/O bitmap Sohil Mehta
@ 2026-08-06 1:15 ` Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 4/7] KVM: VMX: Implement LASS violation check Sohil Mehta
` (3 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Sohil Mehta @ 2026-08-06 1:15 UTC (permalink / raw)
To: kvm, x86
Cc: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H . Peter Anvin, Shuah Khan,
Binbin Wu, Peter Zijlstra, Chang S . Bae, Kai Huang, Fuad Tabba,
Chao Gao, Yosry Ahmed, Claudio Imbrenda, David Matlack,
Bala-Vignesh-Reddy, Kishen Maloor, Rick Edgecombe, Sohil Mehta,
linux-kernel, linux-kselftest
From: Zeng Guang <guang.zeng@intel.com>
When Linear Address Space Separation (LASS) is enabled, the processor
applies a LASS violation check on every access to a linear address. To
align with hardware behavior, KVM needs to perform the same check during
instruction emulation before consulting the page tables.
Add a new callback to x86_emulate_ops to let the emulator query whether
an access would trigger a LASS violation. The callback takes the linear
address and the size that describe the memory access, plus a set of
flags that convey the type of access.
Add the LASS violation check to __linearize() so that every explicit
guest memory access is validated along with the other linear address
checks. The SDM (June 2026), Vol3, Chapter 4, specifically states that
there is no relative ordering between the canonicality check and the
LASS violation check. Also, there is no prioritization specified between
the faults generated by alignment checks and LASS violations.
Implicit supervisor accesses bypass __linearize(). So, add LASS checks
for those in linear_read_system() and linear_write_system() and tag the
access as implicit.
For now, emulator_is_lass_violation() is a no-op. Later, it will be
wired up to the VMX implementation.
Signed-off-by: Zeng Guang <guang.zeng@intel.com>
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v4:
- Switched to using gva_t for the address parameter
- Clarified LASS and canonicality checks and alignment checks ordering
- Reworded the commit message
- Use kvm_x86_call() instead of static_call()
Note, internal AI review warns about missing canonicality checks during
implicit accesses. A valid guest kernel shouldn't trigger canonicality
faults. But for correctness, such checks could be considered. Though,
that is beyond the scope of this series.
---
arch/x86/include/asm/kvm-x86-ops.h | 1 +
arch/x86/include/asm/kvm_host.h | 2 ++
arch/x86/kvm/emulate.c | 17 +++++++++++++++++
arch/x86/kvm/kvm_emulate.h | 3 ++-
arch/x86/kvm/x86.c | 7 +++++++
5 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index e213c9ae3e30..a82b20281f31 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -146,6 +146,7 @@ KVM_X86_OP(complete_emulated_msr)
KVM_X86_OP(vcpu_deliver_sipi_vector)
KVM_X86_OP_OPTIONAL_RET0(vcpu_get_apicv_inhibit_reasons);
KVM_X86_OP_OPTIONAL(get_untagged_addr)
+KVM_X86_OP_OPTIONAL_RET0(is_lass_violation)
KVM_X86_OP_OPTIONAL(alloc_apic_backing_page)
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
KVM_X86_OP_OPTIONAL_RET0(gmem_make_private)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 283847619ff8..d2181a805ace 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1727,6 +1727,8 @@ struct kvm_x86_ops {
unsigned long (*vcpu_get_apicv_inhibit_reasons)(struct kvm_vcpu *vcpu);
gva_t (*get_untagged_addr)(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags);
+ bool (*is_lass_violation)(struct kvm_vcpu *vcpu, gva_t gva,
+ unsigned int size, unsigned int flags);
void *(*alloc_apic_backing_page)(struct kvm_vcpu *vcpu);
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
int (*gmem_make_private)(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 7f04544cfee5..9cbfde649064 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -693,6 +693,16 @@ static __always_inline int __linearize(struct x86_emulate_ctxt *ctxt,
}
break;
}
+
+ /*
+ * LASS and canonicality checks generate the same fault and
+ * their relative order is not architecturally defined. Also,
+ * the SDM doesn't prioritize LASS violations against
+ * alignment-check faults, so any order is legal.
+ */
+ if (ctxt->ops->is_lass_violation(ctxt, la, size, flags))
+ goto bad;
+
if (la & (insn_alignment(ctxt, size) - 1))
return emulate_gp(ctxt, 0);
return X86EMUL_CONTINUE;
@@ -799,6 +809,9 @@ static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel)
static int linear_read_system(struct x86_emulate_ctxt *ctxt, ulong linear,
void *data, unsigned size)
{
+ if (ctxt->ops->is_lass_violation(ctxt, linear, size, X86EMUL_F_IMPLICIT))
+ return emulate_gp(ctxt, 0);
+
return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception, true);
}
@@ -806,6 +819,10 @@ static int linear_write_system(struct x86_emulate_ctxt *ctxt,
ulong linear, void *data,
unsigned int size)
{
+ if (ctxt->ops->is_lass_violation(ctxt, linear, size,
+ X86EMUL_F_IMPLICIT | X86EMUL_F_WRITE))
+ return emulate_gp(ctxt, 0);
+
return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception, true);
}
diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
index 97421b8dde13..f136d3d0ba42 100644
--- a/arch/x86/kvm/kvm_emulate.h
+++ b/arch/x86/kvm/kvm_emulate.h
@@ -249,7 +249,8 @@ struct x86_emulate_ops {
gva_t (*get_untagged_addr)(struct x86_emulate_ctxt *ctxt, gva_t addr,
unsigned int flags);
-
+ bool (*is_lass_violation)(struct x86_emulate_ctxt *ctxt, gva_t addr,
+ unsigned int size, unsigned int flags);
bool (*is_canonical_addr)(struct x86_emulate_ctxt *ctxt, gva_t addr,
unsigned int flags);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index d94b59140c45..70c8439312c3 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5800,6 +5800,12 @@ static gva_t emulator_get_untagged_addr(struct x86_emulate_ctxt *ctxt,
addr, flags);
}
+static bool emulator_is_lass_violation(struct x86_emulate_ctxt *ctxt, gva_t addr,
+ unsigned int size, unsigned int flags)
+{
+ return kvm_x86_call(is_lass_violation)(emul_to_vcpu(ctxt), addr, size, flags);
+}
+
static bool emulator_is_canonical_addr(struct x86_emulate_ctxt *ctxt,
gva_t addr, unsigned int flags)
{
@@ -5859,6 +5865,7 @@ static const struct x86_emulate_ops emulate_ops = {
.get_xcr = emulator_get_xcr,
.set_xcr = emulator_set_xcr,
.get_untagged_addr = emulator_get_untagged_addr,
+ .is_lass_violation = emulator_is_lass_violation,
.is_canonical_addr = emulator_is_canonical_addr,
.page_address_valid = emulator_page_address_valid,
};
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v4 4/7] KVM: VMX: Implement LASS violation check
2026-08-06 1:15 [PATCH v4 0/7] KVM: x86: Add LASS virtualization support Sohil Mehta
` (2 preceding siblings ...)
2026-08-06 1:15 ` [PATCH v4 3/7] KVM: x86: Add LASS violation checks during instruction emulation Sohil Mehta
@ 2026-08-06 1:15 ` Sohil Mehta
2026-08-06 1:52 ` sashiko-bot
2026-08-06 1:15 ` [PATCH v4 5/7] KVM: x86: Virtualize LASS and advertise support to userspace Sohil Mehta
` (2 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Sohil Mehta @ 2026-08-06 1:15 UTC (permalink / raw)
To: kvm, x86
Cc: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H . Peter Anvin, Shuah Khan,
Binbin Wu, Peter Zijlstra, Chang S . Bae, Kai Huang, Fuad Tabba,
Chao Gao, Yosry Ahmed, Claudio Imbrenda, David Matlack,
Bala-Vignesh-Reddy, Kishen Maloor, Rick Edgecombe, Sohil Mehta,
linux-kernel, linux-kselftest
From: Zeng Guang <guang.zeng@intel.com>
Add a VMX implementation of the is_lass_violation() hook to let KVM
detect Linear Address Space Separation (LASS) violations on linear
addresses generated during emulation. LASS uses bit 63 of the linear
address to determine which half of the address space is being targeted,
and reports a violation when that half doesn't match the current
privilege level.
Note, LASS takes effect only in IA-32e mode; it is ignored in legacy
mode. LASS enforcement for supervisor-mode data accesses additionally
requires SMAP to be enabled, and is suppressed for explicit accesses
when RFLAGS.AC=1.
Enforce LASS violations on emulated instruction fetches and data
accesses, including implicit supervisor accesses, so that the mode-based
protections are applied before paging. Also enforce LASS on the linear
addresses consumed by emulated VMX and SGX ENCLS instructions.
Linear addresses used for TLB invalidation (INVLPG, INVPCID, and
INVVPID) and branch targets are not subject to LASS enforcement.
Signed-off-by: Zeng Guang <guang.zeng@intel.com>
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v4:
- Switch to using gva_t for the address argument
- Split patch diff and reworded the commit message
---
arch/x86/kvm/vmx/main.c | 1 +
arch/x86/kvm/vmx/nested.c | 11 ++++-----
arch/x86/kvm/vmx/sgx.c | 3 ++-
arch/x86/kvm/vmx/vmx.c | 47 +++++++++++++++++++++++++++++++++++++++
arch/x86/kvm/vmx/vmx.h | 3 +++
arch/x86/kvm/x86.c | 2 +-
6 files changed, 60 insertions(+), 7 deletions(-)
diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c
index 0ff3230fd95e..3580aada8d2c 100644
--- a/arch/x86/kvm/vmx/main.c
+++ b/arch/x86/kvm/vmx/main.c
@@ -1031,6 +1031,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
.vcpu_deliver_sipi_vector = kvm_vcpu_deliver_sipi_vector,
.get_untagged_addr = vmx_get_untagged_addr,
+ .is_lass_violation = vmx_is_lass_violation,
.mem_enc_ioctl = vt_op_tdx_only(mem_enc_ioctl),
.vcpu_mem_enc_ioctl = vt_op_tdx_only(vcpu_mem_enc_ioctl),
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 7ed79894d11d..61cf20cc4705 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -5299,11 +5299,12 @@ int get_vmx_mem_address(struct kvm_vcpu *vcpu, unsigned long exit_qualification,
*ret = off;
*ret = vmx_get_untagged_addr(vcpu, *ret, 0);
- /* Long mode: #GP(0)/#SS(0) if the memory address is in a
- * non-canonical form. This is the only check on the memory
- * destination for long mode!
+ /*
+ * Long mode: #GP(0)/#SS(0) if the memory address is in a
+ * non-canonical form, or if the access violates LASS.
*/
- exn = is_noncanonical_address(*ret, vcpu, 0);
+ exn = is_noncanonical_address(*ret, vcpu, 0) ||
+ vmx_is_lass_violation(vcpu, *ret, len, 0);
} else {
/*
* When not in long mode, the virtual/linear address is
@@ -6108,7 +6109,7 @@ static int handle_invvpid(struct kvm_vcpu *vcpu)
if (type != VMX_VPID_EXTENT_ALL_CONTEXT && !operand.vpid)
return nested_vmx_fail(vcpu, VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID);
- /* LAM doesn't apply to addresses that are inputs to TLB invalidation. */
+ /* LAM and LASS don't apply to addresses that are inputs to TLB invalidation. */
if (type == VMX_VPID_EXTENT_INDIVIDUAL_ADDR &&
is_noncanonical_invlpg_address(operand.gla, vcpu))
return nested_vmx_fail(vcpu, VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID);
diff --git a/arch/x86/kvm/vmx/sgx.c b/arch/x86/kvm/vmx/sgx.c
index 771c75a58343..4ac305ed6dea 100644
--- a/arch/x86/kvm/vmx/sgx.c
+++ b/arch/x86/kvm/vmx/sgx.c
@@ -39,7 +39,8 @@ static int sgx_get_encls_gva(struct kvm_vcpu *vcpu, unsigned long offset,
fault = true;
} else if (likely(is_64_bit_mode(vcpu))) {
*gva = vmx_get_untagged_addr(vcpu, *gva, 0);
- fault = is_noncanonical_address(*gva, vcpu, 0);
+ fault = is_noncanonical_address(*gva, vcpu, 0) ||
+ vmx_is_lass_violation(vcpu, *gva, size, 0);
} else {
*gva &= 0xffffffff;
fault = (s.unusable) ||
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 973f7e95be65..ecd105f35c78 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -8604,6 +8604,53 @@ gva_t vmx_get_untagged_addr(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags
return (sign_extend64(gva, lam_bit) & ~BIT_ULL(63)) | (gva & BIT_ULL(63));
}
+bool vmx_is_lass_violation(struct kvm_vcpu *vcpu, gva_t gva,
+ unsigned int size, unsigned int flags)
+{
+ const bool is_supervisor_address = !!(gva & BIT_ULL(63));
+ const bool implicit_supervisor = !!(flags & X86EMUL_F_IMPLICIT);
+ const bool fetch = !!(flags & X86EMUL_F_FETCH);
+
+ if (!kvm_is_cr4_bit_set(vcpu, X86_CR4_LASS) || !is_long_mode(vcpu))
+ return false;
+
+ /*
+ * INVLPG isn't subject to LASS, e.g. to allow invalidating userspace
+ * addresses without toggling RFLAGS.AC. Branch targets aren't subject
+ * to LASS in order to simplify far control transfers (the subsequent
+ * fetch will enforce LASS as appropriate).
+ */
+ if (flags & (X86EMUL_F_BRANCH | X86EMUL_F_INVLPG))
+ return false;
+
+ if (!implicit_supervisor && vmx_get_cpl(vcpu) == 3)
+ return is_supervisor_address;
+
+ /*
+ * LASS enforcement for supervisor-mode data accesses depends on SMAP
+ * being enabled, and like SMAP ignores explicit accesses if RFLAGS.AC=1.
+ */
+ if (!fetch) {
+ if (!kvm_is_cr4_bit_set(vcpu, X86_CR4_SMAP))
+ return false;
+
+ if (!implicit_supervisor && (kvm_get_rflags(vcpu) & X86_EFLAGS_AC))
+ return false;
+ }
+
+ /*
+ * The entire access must be in the appropriate address space. Note,
+ * if LAM is supported, @gva has already been untagged, so barring a
+ * massive architecture change to expand the canonical address range,
+ * it's impossible for a user access to straddle user and supervisor
+ * address spaces.
+ */
+ if (size && !((gva + size - 1) & BIT_ULL(63)))
+ return true;
+
+ return !is_supervisor_address;
+}
+
static unsigned int vmx_handle_intel_pt_intr(void)
{
struct kvm_vcpu *vcpu = kvm_get_running_vcpu();
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index dc8517f15bc4..43df725a77f0 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -397,6 +397,9 @@ u64 vmx_get_l2_tsc_multiplier(struct kvm_vcpu *vcpu);
gva_t vmx_get_untagged_addr(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags);
+bool vmx_is_lass_violation(struct kvm_vcpu *vcpu, gva_t gva,
+ unsigned int size, unsigned int flags);
+
void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu);
u64 vmx_get_supported_debugctl(struct kvm_vcpu *vcpu, bool host_initiated);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 70c8439312c3..a6ea736fd48f 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -10752,7 +10752,7 @@ int kvm_handle_invpcid(struct kvm_vcpu *vcpu, unsigned long type, gva_t gva)
switch (type) {
case INVPCID_TYPE_INDIV_ADDR:
/*
- * LAM doesn't apply to addresses that are inputs to TLB
+ * LAM and LASS don't apply to addresses that are inputs to TLB
* invalidation.
*/
if ((!pcid_enabled && (operand.pcid != 0)) ||
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v4 5/7] KVM: x86: Virtualize LASS and advertise support to userspace
2026-08-06 1:15 [PATCH v4 0/7] KVM: x86: Add LASS virtualization support Sohil Mehta
` (3 preceding siblings ...)
2026-08-06 1:15 ` [PATCH v4 4/7] KVM: VMX: Implement LASS violation check Sohil Mehta
@ 2026-08-06 1:15 ` Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 6/7] KVM: selftests: Add coverage for LASS CPUID and CR4 handling Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 7/7] selftests/x86: Add a userspace test for LASS enforcement Sohil Mehta
6 siblings, 0 replies; 10+ messages in thread
From: Sohil Mehta @ 2026-08-06 1:15 UTC (permalink / raw)
To: kvm, x86
Cc: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H . Peter Anvin, Shuah Khan,
Binbin Wu, Peter Zijlstra, Chang S . Bae, Kai Huang, Fuad Tabba,
Chao Gao, Yosry Ahmed, Claudio Imbrenda, David Matlack,
Bala-Vignesh-Reddy, Kishen Maloor, Rick Edgecombe, Sohil Mehta,
linux-kernel, linux-kselftest
From: Zeng Guang <guang.zeng@intel.com>
Virtualize Linear Address Space Separation (LASS) by letting the guest
enable CR4.LASS[bit 27]. Allow the guest to set CR4.LASS only if LASS is
enumerated in the guest's CPUID. Set CR4.LASS in the emulated
IA32_VMX_CR4_FIXED1 MSR so that LASS can be enabled in nested VMX
operation as well.
Keep CR4.LASS KVM-owned instead of making it guest-owned. LASS is
expected to be enabled once per vCPU at boot and rarely toggled at
runtime.
LASS is enumerated by CPUID.(EAX=07H,ECX=1):EAX.LASS[bit 6] and only
works in IA-32e mode. Advertise LASS to userspace on 64-bit kernels and
only when it is supported by the host. Notably, it will not be exposed
to userspace if the deprecated vsyscall=emulate mode is set on the
kernel command line.
Signed-off-by: Zeng Guang <guang.zeng@intel.com>
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v4:
- Split patch diffs and reworded the commit message
- Use X86_64_F() instead of F()
---
arch/x86/kvm/cpuid.c | 1 +
arch/x86/kvm/regs.h | 4 +++-
arch/x86/kvm/vmx/vmx.c | 1 +
3 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 9e9cf6538a96..c4dbd00f5e4e 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1029,6 +1029,7 @@ void kvm_initialize_cpu_caps(void)
F(SM4),
F(AVX_VNNI),
F(AVX512_BF16),
+ X86_64_F(LASS),
F(CMPCCXADD),
F(FZRM),
F(FSRS),
diff --git a/arch/x86/kvm/regs.h b/arch/x86/kvm/regs.h
index 447f0ec3e63e..be8621695c41 100644
--- a/arch/x86/kvm/regs.h
+++ b/arch/x86/kvm/regs.h
@@ -28,7 +28,7 @@ static_assert(!(KVM_POSSIBLE_CR0_GUEST_BITS & X86_CR0_PDPTR_BITS));
| X86_CR4_OSXSAVE | X86_CR4_SMEP | X86_CR4_FSGSBASE \
| X86_CR4_OSXMMEXCPT | X86_CR4_LA57 | X86_CR4_VMXE \
| X86_CR4_SMAP | X86_CR4_PKE | X86_CR4_UMIP \
- | X86_CR4_LAM_SUP | X86_CR4_CET))
+ | X86_CR4_LAM_SUP | X86_CR4_CET | X86_CR4_LASS))
#define CR8_RESERVED_BITS (~(unsigned long)X86_CR8_TPR)
@@ -405,6 +405,8 @@ static inline bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
__reserved_bits |= X86_CR4_PCIDE; \
if (!__cpu_has(__c, X86_FEATURE_LAM)) \
__reserved_bits |= X86_CR4_LAM_SUP; \
+ if (!__cpu_has(__c, X86_FEATURE_LASS)) \
+ __reserved_bits |= X86_CR4_LASS; \
if (!__cpu_has(__c, X86_FEATURE_SHSTK) && \
!__cpu_has(__c, X86_FEATURE_IBT)) \
__reserved_bits |= X86_CR4_CET; \
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index ecd105f35c78..3082195906da 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -7885,6 +7885,7 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu)
entry = kvm_find_cpuid_entry_index(vcpu, 0x7, 1);
cr4_fixed1_update(X86_CR4_LAM_SUP, eax, feature_bit(LAM));
+ cr4_fixed1_update(X86_CR4_LASS, eax, feature_bit(LASS));
#undef cr4_fixed1_update
}
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v4 6/7] KVM: selftests: Add coverage for LASS CPUID and CR4 handling
2026-08-06 1:15 [PATCH v4 0/7] KVM: x86: Add LASS virtualization support Sohil Mehta
` (4 preceding siblings ...)
2026-08-06 1:15 ` [PATCH v4 5/7] KVM: x86: Virtualize LASS and advertise support to userspace Sohil Mehta
@ 2026-08-06 1:15 ` Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 7/7] selftests/x86: Add a userspace test for LASS enforcement Sohil Mehta
6 siblings, 0 replies; 10+ messages in thread
From: Sohil Mehta @ 2026-08-06 1:15 UTC (permalink / raw)
To: kvm, x86
Cc: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H . Peter Anvin, Shuah Khan,
Binbin Wu, Peter Zijlstra, Chang S . Bae, Kai Huang, Fuad Tabba,
Chao Gao, Yosry Ahmed, Claudio Imbrenda, David Matlack,
Bala-Vignesh-Reddy, Kishen Maloor, Rick Edgecombe, Sohil Mehta,
linux-kernel, linux-kselftest
Add a LASS selftest for KVM's handling of CPUID advertisement and
CR4.LASS. Verify that a guest write to CR4.LASS generates #GP, and
leaves CR4 unchanged, when LASS isn't enumerated in the guest's CPUID.
Extend set_sregs_test to cover CR4.LASS and verify that KVM_SET_SREGS
accepts CR4.LASS when LASS is supported, and rejects it, without
modifying sregs, when it isn't.
Don't try running with CR4.LASS enabled in the guest or attempt to
trigger LASS violations. Selftests run guest code in the lower half of
the address space at CPL0, so enabling CR4.LASS would make the next
instruction fetch a violation and triple-fault the guest.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v4:
- New patch
lass_test.c doesn't really test LASS enforcement or any other LASS
functionality. It just tests that a guest write to CR4.LASS generates
a #GP when LASS isn't enumerated. There could be a generic test which
covers such CPUID/CR4 interactions. For now, I added something for LASS
because I couldn't find one.
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/include/x86/processor.h | 2 +
tools/testing/selftests/kvm/x86/lass_test.c | 56 +++++++++++++++++++
.../selftests/kvm/x86/set_sregs_test.c | 3 +
4 files changed, 62 insertions(+)
create mode 100644 tools/testing/selftests/kvm/x86/lass_test.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 00123169a190..86fa93e0f6c0 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -91,6 +91,7 @@ TEST_GEN_PROGS_x86 += x86/hyperv_tlb_flush
TEST_GEN_PROGS_x86 += x86/kvm_clock_test
TEST_GEN_PROGS_x86 += x86/kvm_pv_test
TEST_GEN_PROGS_x86 += x86/kvm_buslock_test
+TEST_GEN_PROGS_x86 += x86/lass_test
TEST_GEN_PROGS_x86 += x86/monitor_mwait_test
TEST_GEN_PROGS_x86 += x86/msrs_test
TEST_GEN_PROGS_x86 += x86/nested_close_kvm_test
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 6e6f70035508..f425166174f9 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -79,6 +79,7 @@ const char *ex_str(int vector);
#define X86_CR4_SMEP (1ul << 20)
#define X86_CR4_SMAP (1ul << 21)
#define X86_CR4_PKE (1ul << 22)
+#define X86_CR4_LASS (1ul << 27)
struct xstate_header {
u64 xstate_bv;
@@ -195,6 +196,7 @@ struct kvm_x86_cpu_feature {
#define X86_FEATURE_SPEC_CTRL KVM_X86_CPU_FEATURE(0x7, 0, EDX, 26)
#define X86_FEATURE_ARCH_CAPABILITIES KVM_X86_CPU_FEATURE(0x7, 0, EDX, 29)
#define X86_FEATURE_PKS KVM_X86_CPU_FEATURE(0x7, 0, ECX, 31)
+#define X86_FEATURE_LASS KVM_X86_CPU_FEATURE(0x7, 1, EAX, 6)
#define X86_FEATURE_XTILECFG KVM_X86_CPU_FEATURE(0xD, 0, EAX, 17)
#define X86_FEATURE_XTILEDATA KVM_X86_CPU_FEATURE(0xD, 0, EAX, 18)
#define X86_FEATURE_XSAVES KVM_X86_CPU_FEATURE(0xD, 1, EAX, 3)
diff --git a/tools/testing/selftests/kvm/x86/lass_test.c b/tools/testing/selftests/kvm/x86/lass_test.c
new file mode 100644
index 000000000000..7c5cfa24c2bd
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/lass_test.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Linear Address Space Separation (LASS) test
+ *
+ * Copyright (C) 2026, Intel Corporation.
+ *
+ * Only test that the guest can't set CR4.LASS when LASS isn't
+ * enumerated in the guest's CPUID. KVM's handling of CR4.LASS via
+ * KVM_SET_SREGS is covered by set_sregs_test.
+ *
+ * Testing LASS enforcement requires running supervisor code in the
+ * upper half of the address space, which the KVM selftests framework
+ * doesn't support. Enabling CR4.LASS in the current framework would
+ * make the next instruction fetch a violation and triple-fault the
+ * guest.
+ */
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+
+/*
+ * Without LASS in CPUID, a guest write must generate #GP without
+ * changing CR4. Reserved bits are owned by KVM so the write is
+ * guaranteed to exit to KVM.
+ */
+static void guest_code(void)
+{
+ u8 vector;
+
+ GUEST_ASSERT(!this_cpu_has(X86_FEATURE_LASS));
+
+ vector = kvm_asm_safe("mov %[cr4], %%cr4",
+ [cr4] "r"(get_cr4() | X86_CR4_LASS));
+ __GUEST_ASSERT(vector == GP_VECTOR,
+ "Wanted #GP on CR4.LASS, got %s", ex_str(vector));
+ GUEST_ASSERT(!(get_cr4() & X86_CR4_LASS));
+
+ GUEST_DONE();
+}
+
+int main(int argc, char *argv[])
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+
+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_LASS));
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_LASS);
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE);
+
+ kvm_vm_free(vm);
+ return 0;
+}
diff --git a/tools/testing/selftests/kvm/x86/set_sregs_test.c b/tools/testing/selftests/kvm/x86/set_sregs_test.c
index 603226ffe437..8b375fea84d0 100644
--- a/tools/testing/selftests/kvm/x86/set_sregs_test.c
+++ b/tools/testing/selftests/kvm/x86/set_sregs_test.c
@@ -72,6 +72,8 @@ static u64 calc_supported_cr4_feature_bits(void)
cr4 |= X86_CR4_SMAP;
if (kvm_cpu_has(X86_FEATURE_PKU))
cr4 |= X86_CR4_PKE;
+ if (kvm_cpu_has(X86_FEATURE_LASS))
+ cr4 |= X86_CR4_LASS;
return cr4;
}
@@ -128,6 +130,7 @@ static void test_cr_bits(struct kvm_vcpu *vcpu, u64 cr4)
TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_SMEP);
TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_SMAP);
TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_PKE);
+ TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_LASS);
for (i = 32; i < 64; i++)
TEST_INVALID_SREG_BIT(vcpu, cr0, sregs, BIT(i));
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v4 7/7] selftests/x86: Add a userspace test for LASS enforcement
2026-08-06 1:15 [PATCH v4 0/7] KVM: x86: Add LASS virtualization support Sohil Mehta
` (5 preceding siblings ...)
2026-08-06 1:15 ` [PATCH v4 6/7] KVM: selftests: Add coverage for LASS CPUID and CR4 handling Sohil Mehta
@ 2026-08-06 1:15 ` Sohil Mehta
6 siblings, 0 replies; 10+ messages in thread
From: Sohil Mehta @ 2026-08-06 1:15 UTC (permalink / raw)
To: kvm, x86
Cc: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H . Peter Anvin, Shuah Khan,
Binbin Wu, Peter Zijlstra, Chang S . Bae, Kai Huang, Fuad Tabba,
Chao Gao, Yosry Ahmed, Claudio Imbrenda, David Matlack,
Bala-Vignesh-Reddy, Kishen Maloor, Rick Edgecombe, Sohil Mehta,
linux-kernel, linux-kselftest
With LASS enabled, a user-mode access to a kernel address raises a #GP
instead of the #PF that SMAP/SMEP would produce. Nothing in the x86
selftests specifically tests for a LASS violation. The vsyscall selftest
exercises this flow but doesn't verify the resulting #GP.
Add a test that reads, writes and executes at a canonical kernel address
and verifies each one faults with a #GP and a null error code. For the
instruction fetch, also verify the fault is reported at the target,
since LASS does not check the target of a branch.
Skip the test unless /proc/cpuinfo reports the lass flag. The CPUID bit
alone does not say whether the kernel enabled LASS.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v4:
- New patch
---
tools/testing/selftests/x86/Makefile | 3 +-
tools/testing/selftests/x86/lass.c | 196 +++++++++++++++++++++++++++
2 files changed, 198 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/x86/lass.c
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 434065215d12..252d757fc1b2 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -19,7 +19,8 @@ TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \
test_FCMOV test_FCOMI test_FISTTP \
vdso_restorer
TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip syscall_numbering \
- corrupt_xstate_header amx lam test_shadow_stack avx apx
+ corrupt_xstate_header amx lam test_shadow_stack avx apx \
+ lass
# Some selftests require 32bit support enabled also on 64bit systems
TARGETS_C_32BIT_NEEDED := ldt_gdt ptrace_syscall
diff --git a/tools/testing/selftests/x86/lass.c b/tools/testing/selftests/x86/lass.c
new file mode 100644
index 000000000000..3dd3dc8e41d1
--- /dev/null
+++ b/tools/testing/selftests/x86/lass.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * lass.c - Test Linear Address Space Separation (LASS) enforcement
+ *
+ * With LASS enabled, a user-mode read, write or instruction fetch at a
+ * kernel address raises a #GP instead of the #PF that SMAP/SMEP would
+ * produce.
+ */
+#define _GNU_SOURCE
+
+#include <setjmp.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ucontext.h>
+
+#include "helpers.h"
+
+#ifndef __x86_64__
+# error This test is 64-bit only
+#endif
+
+/*
+ * LASS rejects an address based on bit 63 alone, but a non-canonical
+ * address raises the very same #GP for a different reason, so the
+ * address has to be canonical to attribute the fault to LASS.
+ *
+ * Bits 63:47 are all set here, which is canonical with 4-level paging
+ * as well as 5-level paging.
+ */
+#define KERNEL_ADDR 0xffff800000000000UL
+
+static sigjmp_buf jmpbuf;
+
+static volatile unsigned long fault_trapno, fault_err, fault_rip;
+
+/* Handle SIGSEGV (#GP and #PF) as well as SIGBUS (#SS) */
+static void fault_handler(int sig, siginfo_t *info, void *ctx_void)
+{
+ ucontext_t *ctx = (ucontext_t *)ctx_void;
+
+ fault_trapno = ctx->uc_mcontext.gregs[REG_TRAPNO];
+ fault_err = ctx->uc_mcontext.gregs[REG_ERR];
+ fault_rip = ctx->uc_mcontext.gregs[REG_RIP];
+ siglongjmp(jmpbuf, 1);
+}
+
+static bool is_lass_active(void)
+{
+ static const char delims[] = " \n";
+ unsigned int eax, ebx, ecx, edx;
+ bool found = false;
+ char line[4096];
+ FILE *cpuinfo;
+
+ /*
+ * Only the cpuinfo flag reflects whether the kernel actually
+ * enabled LASS.
+ */
+ cpuinfo = fopen("/proc/cpuinfo", "r");
+ if (!cpuinfo)
+ ksft_exit_fail_msg("failed to open /proc/cpuinfo\n");
+
+ while (!found && fgets(line, sizeof(line), cpuinfo)) {
+ char *flag;
+
+ if (strncmp(line, "flags", 5))
+ continue;
+
+ /* Match whole words only, not a substring of another flag. */
+ for (flag = strtok(line, delims); flag; flag = strtok(NULL, delims)) {
+ if (!strcmp(flag, "lass")) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ fclose(cpuinfo);
+
+ if (found)
+ return true;
+
+ /* Check CPUID.(EAX=07H,ECX=1):EAX.LASS[bit 6] */
+ __cpuid_count(0x7, 0x1, eax, ebx, ecx, edx);
+ if (eax & (1 << 6))
+ ksft_print_msg("LASS is supported by the CPU but not enabled by the kernel\n");
+
+ return false;
+}
+
+/* General Protection Fault (trapnr.h is not exported to uapi) */
+#define X86_TRAP_GP 13
+
+/* A LASS violation raises a #GP with a null error code. */
+static bool is_lass_violation(void)
+{
+ return fault_trapno == X86_TRAP_GP && !fault_err;
+}
+
+static void test_kernel_read(void)
+{
+ if (sigsetjmp(jmpbuf, 1) == 0) {
+ *(volatile unsigned long *)KERNEL_ADDR;
+ ksft_test_result_fail("the read did not fault\n");
+ return;
+ }
+
+ ksft_test_result(is_lass_violation(),
+ "the read faulted with trap=%ld, error=0x%lx\n",
+ fault_trapno, fault_err);
+}
+
+static void test_kernel_write(void)
+{
+ if (sigsetjmp(jmpbuf, 1) == 0) {
+ *(volatile unsigned long *)KERNEL_ADDR = 0x1a55;
+ ksft_test_result_fail("the write did not fault\n");
+ return;
+ }
+
+ ksft_test_result(is_lass_violation(),
+ "the write faulted with trap=%ld, error=0x%lx\n",
+ fault_trapno, fault_err);
+}
+
+/*
+ * Use inline asm rather than a call through a function pointer: a direct
+ * 'call rel32' cannot reach a kernel address, and letting the compiler lower
+ * the indirect branch risks routing it through a thunk, or eliding it
+ * altogether, either of which would stop testing the fetch.
+ */
+static void do_fetch(unsigned long addr)
+{
+ asm volatile ("call *%[fn]"
+ : : [fn] "r" (addr)
+ : "memory", "cc", "rax", "rcx", "rdx", "rsi", "rdi",
+ "r8", "r9", "r10", "r11");
+}
+
+static void test_kernel_fetch(void)
+{
+ if (sigsetjmp(jmpbuf, 1) == 0) {
+ do_fetch(KERNEL_ADDR);
+
+ /*
+ * Execution resumed at an unknown point with an undefined
+ * register state, so don't try to run the rest of the tests.
+ */
+ ksft_exit_fail_msg("the fetch returned without faulting\n");
+ }
+
+ /*
+ * Branch instructions do not check their target against LASS. The
+ * violation happens when the target address is used to fetch the
+ * next instruction, so the fault must be reported at the target
+ * rather than at the branch.
+ */
+ if (fault_rip != KERNEL_ADDR) {
+ ksft_test_result_fail("the fetch faulted at RIP 0x%lx instead of 0x%lx\n",
+ fault_rip, (unsigned long)KERNEL_ADDR);
+ return;
+ }
+
+ ksft_test_result(is_lass_violation(),
+ "the fetch faulted with trap=%ld, error=0x%lx\n",
+ fault_trapno, fault_err);
+}
+
+#define TOTAL_TESTS 3
+
+int main(void)
+{
+ ksft_print_header();
+
+ if (!is_lass_active())
+ ksft_exit_skip("LASS is not enabled\n");
+
+ ksft_set_plan(TOTAL_TESTS);
+
+ sethandler(SIGSEGV, fault_handler, 0);
+ /* Only to report a #SS; LASS shouldn't cause one here. */
+ sethandler(SIGBUS, fault_handler, 0);
+
+ ksft_print_msg("Accessing the kernel address 0x%lx from userspace\n",
+ (unsigned long)KERNEL_ADDR);
+ test_kernel_read();
+ test_kernel_write();
+ test_kernel_fetch();
+
+ clearhandler(SIGBUS);
+ clearhandler(SIGSEGV);
+
+ ksft_finished();
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v4 4/7] KVM: VMX: Implement LASS violation check
2026-08-06 1:15 ` [PATCH v4 4/7] KVM: VMX: Implement LASS violation check Sohil Mehta
@ 2026-08-06 1:52 ` sashiko-bot
2026-08-07 1:42 ` Sohil Mehta
0 siblings, 1 reply; 10+ messages in thread
From: sashiko-bot @ 2026-08-06 1:52 UTC (permalink / raw)
To: Sohil Mehta; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] LASS enforcement for supervisor-mode instruction fetches incorrectly ignores whether SMEP is enabled.
- [High] LASS is incorrectly enforced in compatibility mode, leading to spurious #GP faults for supervisor accesses.
--
--- Patch [4]: [PATCH v4 4/7] KVM: VMX: Implement LASS violation check ---
commit 7357ad469a8f5e7cd4a7e4ed10750c1068c7cc69
Author: Zeng Guang <guang.zeng@intel.com>
KVM: VMX: Implement LASS violation check
This commit adds a VMX implementation for detecting Linear Address Space
Separation (LASS) violations during instruction emulation. It enforces
mode-based protections on emulated instruction fetches, data accesses,
and VMX/SGX ENCLS instructions. LASS enforcement relies on CR4 and RFLAGS
settings, applying restrictions selectively based on execution context.
> --- a/arch/x86/kvm/vmx/vmx.c
> +++ b/arch/x86/kvm/vmx/vmx.c
[ ... ]
> @@ -8604,6 +8604,53 @@ gva_t vmx_get_untagged_addr(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags
> return (sign_extend64(gva, lam_bit) & ~BIT_ULL(63)) | (gva & BIT_ULL(63));
> }
>
> +bool vmx_is_lass_violation(struct kvm_vcpu *vcpu, gva_t gva,
> + unsigned int size, unsigned int flags)
> +{
> + const bool is_supervisor_address = !!(gva & BIT_ULL(63));
> + const bool implicit_supervisor = !!(flags & X86EMUL_F_IMPLICIT);
> + const bool fetch = !!(flags & X86EMUL_F_FETCH);
> +
> + if (!kvm_is_cr4_bit_set(vcpu, X86_CR4_LASS) || !is_long_mode(vcpu))
> + return false;
[Severity: High]
Should this check use is_64_bit_mode() instead of is_long_mode()?
If a guest is in compatibility mode with LASS and SMAP enabled, a compatibility
mode application might trigger an interrupt resulting in an implicit supervisor
access to the IDT or TSS. Since compatibility-mode linear addresses are 32-bit,
bit 63 is always 0.
Does using is_long_mode() cause the hypervisor to erroneously flag these as
LASS violations for supervisor mode, breaking 32-bit execution?
> +
> + /*
> + * INVLPG isn't subject to LASS, e.g. to allow invalidating userspace
> + * addresses without toggling RFLAGS.AC. Branch targets aren't subject
> + * to LASS in order to simplify far control transfers (the subsequent
> + * fetch will enforce LASS as appropriate).
> + */
> + if (flags & (X86EMUL_F_BRANCH | X86EMUL_F_INVLPG))
> + return false;
> +
> + if (!implicit_supervisor && vmx_get_cpl(vcpu) == 3)
> + return is_supervisor_address;
> +
> + /*
> + * LASS enforcement for supervisor-mode data accesses depends on SMAP
> + * being enabled, and like SMAP ignores explicit accesses if RFLAGS.AC=1.
> + */
> + if (!fetch) {
> + if (!kvm_is_cr4_bit_set(vcpu, X86_CR4_SMAP))
> + return false;
> +
> + if (!implicit_supervisor && (kvm_get_rflags(vcpu) & X86_EFLAGS_AC))
> + return false;
> + }
[Severity: High]
Does LASS enforcement for supervisor-mode instruction fetches require a check
for SMEP being enabled here?
According to the architecture specification, LASS applies to supervisor
instruction fetches only if SMEP is enabled, similar to how it requires
SMAP for data accesses.
If a guest OS running in 64-bit mode with CR4.LASS = 1 and CR4.SMEP = 0
performs a supervisor-mode instruction fetch from a user-mode address,
will omitting a kvm_is_cr4_bit_set(vcpu, X86_CR4_SMEP) check when fetch
is true cause a spurious #GP fault to be injected?
> +
> + /*
> + * The entire access must be in the appropriate address space. Note,
> + * if LAM is supported, @gva has already been untagged, so barring a
> + * massive architecture change to expand the canonical address range,
> + * it's impossible for a user access to straddle user and supervisor
> + * address spaces.
> + */
> + if (size && !((gva + size - 1) & BIT_ULL(63)))
> + return true;
> +
> + return !is_supervisor_address;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260806011536.4172258-1-sohil.mehta@intel.com?part=4
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v4 4/7] KVM: VMX: Implement LASS violation check
2026-08-06 1:52 ` sashiko-bot
@ 2026-08-07 1:42 ` Sohil Mehta
0 siblings, 0 replies; 10+ messages in thread
From: Sohil Mehta @ 2026-08-07 1:42 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm
On 8/5/2026 6:52 PM, sashiko-bot@kernel.org wrote:
>> @@ -8604,6 +8604,53 @@ gva_t vmx_get_untagged_addr(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags
>> return (sign_extend64(gva, lam_bit) & ~BIT_ULL(63)) | (gva & BIT_ULL(63));
>> }
>>
>> +bool vmx_is_lass_violation(struct kvm_vcpu *vcpu, gva_t gva,
>> + unsigned int size, unsigned int flags)
>> +{
>> + const bool is_supervisor_address = !!(gva & BIT_ULL(63));
>> + const bool implicit_supervisor = !!(flags & X86EMUL_F_IMPLICIT);
>> + const bool fetch = !!(flags & X86EMUL_F_FETCH);
>> +
>> + if (!kvm_is_cr4_bit_set(vcpu, X86_CR4_LASS) || !is_long_mode(vcpu))
>> + return false;
>
> [Severity: High]
> Should this check use is_64_bit_mode() instead of is_long_mode()?
>
No, LASS applies only to IA-32e mode. So, I think the is_long_mode()
check here is correct.
> If a guest is in compatibility mode with LASS and SMAP enabled, a compatibility
> mode application might trigger an interrupt resulting in an implicit supervisor
> access to the IDT or TSS. Since compatibility-mode linear addresses are 32-bit,
> bit 63 is always 0.
>
AFAIU, in IA-32e compatiblity mode, all the bases (GDTR, IDTR, LDTR, and
TR) hold the full 64-bit address. So, there isn't any truncation and an
implicit supervisor access should have bit 63 present (set as 1).
> Does using is_long_mode() cause the hypervisor to erroneously flag these as
> LASS violations for supervisor mode, breaking 32-bit execution?
>
...
>> +
>> + /*
>> + * LASS enforcement for supervisor-mode data accesses depends on SMAP
>> + * being enabled, and like SMAP ignores explicit accesses if RFLAGS.AC=1.
>> + */
>> + if (!fetch) {
>> + if (!kvm_is_cr4_bit_set(vcpu, X86_CR4_SMAP))
>> + return false;
>> +
>> + if (!implicit_supervisor && (kvm_get_rflags(vcpu) & X86_EFLAGS_AC))
>> + return false;
>> + }
>
> [Severity: High]
> Does LASS enforcement for supervisor-mode instruction fetches require a check
> for SMEP being enabled here?
> > According to the architecture specification, LASS applies to supervisor
> instruction fetches only if SMEP is enabled, similar to how it requires
> SMAP for data accesses.
>
No, an SMEP check is not needed during LASS enforcement. The SDM
specifically states that:
"LASS enforces the equivalent of supervisor-mode execution prevention
regardless of the setting of CR4.SMEP[bit 17]"
"A supervisor-mode instruction fetch causes a LASS violation if it would
accesses a linear address of which bit 63 is 0. (Unlike paging, this
behavior of LASS applies regardless of the setting of CR4.SMEP.)"
So, an SMEP check would actually be wrong.
> If a guest OS running in 64-bit mode with CR4.LASS = 1 and CR4.SMEP = 0
> performs a supervisor-mode instruction fetch from a user-mode address,
> will omitting a kvm_is_cr4_bit_set(vcpu, X86_CR4_SMEP) check when fetch
> is true cause a spurious #GP fault to be injected?
>
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-08-07 1:42 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-06 1:15 [PATCH v4 0/7] KVM: x86: Add LASS virtualization support Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 1/7] KVM: x86: Add an emulator flag to differentiate branch targets from fetches Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 2/7] KVM: x86: Use linear_read_system() to read the TSS I/O bitmap Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 3/7] KVM: x86: Add LASS violation checks during instruction emulation Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 4/7] KVM: VMX: Implement LASS violation check Sohil Mehta
2026-08-06 1:52 ` sashiko-bot
2026-08-07 1:42 ` Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 5/7] KVM: x86: Virtualize LASS and advertise support to userspace Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 6/7] KVM: selftests: Add coverage for LASS CPUID and CR4 handling Sohil Mehta
2026-08-06 1:15 ` [PATCH v4 7/7] selftests/x86: Add a userspace test for LASS enforcement Sohil Mehta
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox