Re: [PATCH] mm/vmalloc: widen guard region to defeat ENTER-based stack pivot
From: Pedro Falcato
Date: Fri Jun 26 2026 - 15:39:55 EST
On Fri, Jun 26, 2026 at 10:34:44AM -0700, Xiang Mei wrote:
> With CONFIG_VMAP_STACK, kernel stacks are allocated in the vmalloc area,
> which an unprivileged user can surround with attacker-controlled data by
> spraying vmap allocations adjacent to a target stack (for example via
> XDP_UMEM_REG, though other vmalloc spray paths work too). Today each
> guarded vmalloc allocation is followed by a single unmapped guard page.
>
> A single guard page is not enough to contain the x86_64 ENTER
> instruction used as a one-instruction stack pivot. ENTER imm16, imm8
> builds a stack frame and lowers RSP by:
>
> imm16 + 8 * (L + 1), L = imm8 & 0x1f
>
> imm16 is an unsigned 16-bit operand (ENTER never raises RSP), and L is
> in [0, 31], so the maximum displacement of a single ENTER is:
>
> 0xffff + 8 * 0x20 = 0x100ff bytes
>
> That is more than enough to step off the current stack, across the
> one-page guard, and into the adjacent sprayed pages. When those pages
> contain a return sled feeding a ROP chain, reaching any ENTER gadget
> (opcode 0xc8, abundant as both intended and unintended gadgets) turns a
> control-flow hijack into full ROP execution without any register control
> at the hijack site, making it a one-gadget-style primitive that
> significantly eases exploitation. The pivot happens after the control
> transfer, so it is not constrained by CFI (kCFI/FineIBT).
>
> Widen the guard region from one page to VMAP_GUARD_PAGES (0x11 pages,
> 0x11000 bytes), which is the smallest whole-page span exceeding the
> 0x100ff-byte maximum single-ENTER pivot. A pivot off the top of the
> stack now lands in the unmapped guard and faults, instead of in mapped,
> attacker-controlled memory. RANDOMIZE_KSTACK_OFFSET only perturbs RSP by
> a sub-page amount, so it does not change the required width.
What's so special about enter? Why do we need to design our guard pages
around it? FWIW, I can't find enter instructions in any of my kernel builds,
nor can I convince gcc (on godbolt) to generate an enter instruction.
If it's just "this is a single instruction that adjusts RSP", why is e.g.
sub imm32, %rsp ok?
FTR, I think it's fine that you're proposing more guard pages; virtual address
space is virtually free on 64-bit architectures (apart from lower page table
density, which may take a little toll on memory usage and/or page table
caching). I'm just wondering why enter is being used as the concrete target
for this.
--
Pedro