Skip to content

Commit b3a7e97

Browse files
sohilmehhansendc
authored andcommitted
x86/alternatives: Disable LASS when patching kernel code
For patching, the kernel initializes a temporary mm area in the lower half of the address range. LASS blocks these accesses because its enforcement relies on bit 63 of the virtual address as opposed to SMAP which depends on the _PAGE_BIT_USER bit in the page table. Disable LASS enforcement by toggling the RFLAGS.AC bit during patching to avoid triggering a #GP fault. Introduce LASS-specific STAC/CLAC helpers to set the AC bit only on platforms that need it. Name the wrappers as lass_stac()/_clac() instead of lass_disable()/_enable() because they only control the kernel data access enforcement. The entire LASS mechanism (including instruction fetch enforcement) is controlled by the CR4.LASS bit. Describe the usage of the new helpers in comparison to the ones used for SMAP. Also, add comments to explain when the existing stac()/clac() should be used. While at it, move the duplicated "barrier" comment to the same block. The Text poking functions use standard memcpy()/memset() while patching kernel code. However, objtool complains about calling such dynamic functions within an AC=1 region. See warning #9, regarding function calls with UACCESS enabled, in tools/objtool/Documentation/objtool.txt. To pacify objtool, one option is to add memcpy() and memset() to the list of allowed-functions. However, that would provide a blanket exemption for all usages of memcpy() and memset(). Instead, replace the standard calls in the text poking functions with their unoptimized, always-inlined versions. Considering that patching is usually small, there is no performance impact expected. Signed-off-by: Sohil Mehta <sohil.mehta@intel.com> Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com> Link: https://patch.msgid.link/20251118182911.2983253-5-sohil.mehta%40intel.com
1 parent d9a96cc commit b3a7e97

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

arch/x86/include/asm/smap.h

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,55 @@
2323

2424
#else /* __ASSEMBLER__ */
2525

26+
/*
27+
* The CLAC/STAC instructions toggle the enforcement of
28+
* X86_FEATURE_SMAP along with X86_FEATURE_LASS.
29+
*
30+
* SMAP enforcement is based on the _PAGE_BIT_USER bit in the page
31+
* tables. The kernel is not allowed to touch pages with that bit set
32+
* unless the AC bit is set.
33+
*
34+
* Use stac()/clac() when accessing userspace (_PAGE_USER) mappings,
35+
* regardless of location.
36+
*
37+
* Note: a barrier is implicit in alternative().
38+
*/
39+
2640
static __always_inline void clac(void)
2741
{
28-
/* Note: a barrier is implicit in alternative() */
2942
alternative("", "clac", X86_FEATURE_SMAP);
3043
}
3144

3245
static __always_inline void stac(void)
3346
{
34-
/* Note: a barrier is implicit in alternative() */
3547
alternative("", "stac", X86_FEATURE_SMAP);
3648
}
3749

50+
/*
51+
* LASS enforcement is based on bit 63 of the virtual address. The
52+
* kernel is not allowed to touch memory in the lower half of the
53+
* virtual address space.
54+
*
55+
* Use lass_stac()/lass_clac() to toggle the AC bit for kernel data
56+
* accesses (!_PAGE_USER) that are blocked by LASS, but not by SMAP.
57+
*
58+
* Even with the AC bit set, LASS will continue to block instruction
59+
* fetches from the user half of the address space. To allow those,
60+
* clear CR4.LASS to disable the LASS mechanism entirely.
61+
*
62+
* Note: a barrier is implicit in alternative().
63+
*/
64+
65+
static __always_inline void lass_clac(void)
66+
{
67+
alternative("", "clac", X86_FEATURE_LASS);
68+
}
69+
70+
static __always_inline void lass_stac(void)
71+
{
72+
alternative("", "stac", X86_FEATURE_LASS);
73+
}
74+
3875
static __always_inline unsigned long smap_save(void)
3976
{
4077
unsigned long flags;

arch/x86/kernel/alternative.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2469,16 +2469,30 @@ void __init_or_module text_poke_early(void *addr, const void *opcode,
24692469
__ro_after_init struct mm_struct *text_poke_mm;
24702470
__ro_after_init unsigned long text_poke_mm_addr;
24712471

2472+
/*
2473+
* Text poking creates and uses a mapping in the lower half of the
2474+
* address space. Relax LASS enforcement when accessing the poking
2475+
* address.
2476+
*
2477+
* objtool enforces a strict policy of "no function calls within AC=1
2478+
* regions". Adhere to the policy by using inline versions of
2479+
* memcpy()/memset() that will never result in a function call.
2480+
*/
2481+
24722482
static void text_poke_memcpy(void *dst, const void *src, size_t len)
24732483
{
2474-
memcpy(dst, src, len);
2484+
lass_stac();
2485+
__inline_memcpy(dst, src, len);
2486+
lass_clac();
24752487
}
24762488

24772489
static void text_poke_memset(void *dst, const void *src, size_t len)
24782490
{
24792491
int c = *(const int *)src;
24802492

2481-
memset(dst, c, len);
2493+
lass_stac();
2494+
__inline_memset(dst, c, len);
2495+
lass_clac();
24822496
}
24832497

24842498
typedef void text_poke_f(void *dst, const void *src, size_t len);

0 commit comments

Comments
 (0)