What keeps kernel shadow stack effective against kernel exploits?
This post introduces one of the virtualization features needed to keep kernel-mode shadow stack functional against kernel exploits: supervisor shadow stack restrictions / supervisor shadow-stack control.
Table of contents👇
Shadow stack
Shadow stack, also referred to as SHSTK, is a backward-edge code flow integrity protection feature available in both Intel and AMD processors. Shadow stack detects the corruption of a return address in the stack by pushing a return address into the secondary stack in addition to the normal stack on the CALL
instruction, and verifying return addresses in both the secondary and normal stacks match on the (I)RET
instruction. If they do not, it indicates stack corruption and #CP is raised to abort the program or kernel.
This secondary stack is called shadow stack, and shadow stack used for kernel-mode CALL
and (I)RET
is called supervisor shadow stack (SSS).
Who keeps shadow stack effective?
User-mode shadow stack
Shadow stack can be enabled for user- and kernel-mode separately. When it is enabled for user-mode, it is the kernel that is responsible for making it functional and resilient against user-mode exploits. It makes sense and is straightforward as shadow stack is configured through system registers (CR4 and MSRs) and page tables, neither of those are accessible from user-mode exploits.
Kernel-mode shadow stack
On the other hand, when shadow stack is enabled for kernel-mode, the kernel alone cannot make it resilient against the kernel-mode exploits, which have access to those configuration nobs.
Let us think through a hypothetical example. We assume the supervisor shadow stack restrictions / supervisor shadow-stack control feature does not exist.
An attacker compromised a user-mode process, exploited a kernel bug, and gained an arbitrary kernel memory read/write primitive. The kernel text is static; meaning they are read-only through the second-level address translation (SLAT) with a hypervisor, and also, impossible to load a new, malicious kernel module. Now, attacker’s options are limited to code-reuse and data-only attacks. To carry out a code-reuse attack, the attacker may suspend a thread, and identify and modify its kernel stack to build a ROP chain. If shadow stack were enabled for kernel-mode, this would not work because return addresses in SSS and normal kernel stack would not match.
If this were the end of the story, security would be a bit of a boring field. An attacker can circumvent the kernel-mode shadow stack in two ways.
Attack by directly modifying a SSS page
Contents of the SSS page can be modified if page tables and the SSS page itself are writable to the kernel privilege exploit. While SSS pages must be read-only at the guest page table level, it is only enforced during page table walk. The attacker can update PTEs to make the SSS page writable, write a desired return address into it, and revert the permission to be read-only. Even having a hypervisor may not help the situation as we will see shortly.
Attack by remapping a SSS page
Additionally, the contents of the SSS page can be changed as long as page tables alone are writable to the kernel privilege exploit. In this case, the attacker can,
- find an arbitrary writable page
- write a desired return address into it
- identify the page’s physical address
- change a PTE of the SSS page to remap it into the identified physical address at (3)
Now, the SSS page is backed by the page where the attacker wrote a desired return address, resulting in the same effect as the first case. Again, having a hypervisor does not necessarily help.
(C’mon. It’s child’s play)
Hypervisor may not help
Having a hypervisor does not necessarily address these issues because the SSS pages cannot be made read-only through SLAT without (probably significant) performance penalties. If you do that, every CALL
would cause VM exit due to an attempt to write a return address into a now-read-only SSS page.
Similarly, making the guest page tables read-only through SLAT will have an additional performance penalty whenever the guest OS updates its page tables. Intel VT-rp would address this problem, but its availability is limited.
Supervisor shadow stack restrictions / supervisor shadow-stack control
The solution to those issues is the feature called supervisor shadow stack restrictions (or SSSCheck) on AMD and supervisor shadow-stack control on Intel processors.
Both effectively do the same things:
- allows a processor to write return addresses into SSS pages marked as read-only with SLAT
- restricts a processor from reading and writing return addresses only from/to pages that are explicitly designated as SSS pages with SLAT
The first one addresses the issue with writable SSS pages. This allows the hypervisor to make the SSS pages read-only while still avoiding unwanted VM exits due to the CALL
instruction writing a return address.
The second one addresses the issue of remapping the SSS pages. This is achieved by the hypervisor designating physical addresses to be SSS pages with SLAT and protecting a processor from checking return addresses in pages not designed as SSS pages. If an attacker remaps the SSS page into another physical address not designated as an SSS page, it will violate this restriction.
Implementation
Availability
The feature has been available since Zen3 (AMD) and 11th gen (Intel). It can be confirmed by testing if the following bit is 1:
- AMD: The bit 19 of CPUID
Fn8000_000A_EDX
(SSSCheck) - Intel: The bit 23 of
IA32_VMX_EPT_VPID_CAP
Enablement
The feature is enabled by setting 1 to the following bits:
- AMD: The bit 4 of VMCB +0x90 (SSSCheckEn)
- Intel: The bit 7 of extended-page-table pointer (EPTP)
Designating the SSS pages
The feature takes effect by specifying physical addresses as the SSS pages by configuring SLAT entries in specific ways.
- AMD:
- NX=1 and U/S=0 in the final nested page table entry, and
- R/W=1 in all other nested non-leaf page table entries leading to the final nested page table entry
- Intel:
- SSS=1 (bit 60) in the final extended page table entry
Those pages should also be marked as read-only to prevent the attacker from directly modifying the contents (*1).
Use in the field
To my knowledge, as of March 16, 2025, Windows is the only platform using this feature. This is not surprising, as very few x86 platforms enable kernel shadow stack and have hypervisor-based security.
Here is an excerpt of dump of SLAT on Windows on both AMD and Intel, showing a few 2MB regions designated as SSS pages (indicated with S
in the right side).
amd_asid_1.log:
0x121e00000 - 0x122000000 -> Identity S-----KR-V
0x137200000 - 0x137400000 -> Identity S-----KR-V
0x31b000000 - 0x31b200000 -> Identity S-----KR-V
0x35e600000 - 0x35e800000 -> Identity S-----KR-V
0x3c7400000 - 0x3c7600000 -> Identity S-----KR-V
0x3f4c00000 - 0x3f4e00000 -> Identity S-----KR-V
intel_vpid_1.log:
0x122400000 - 0x122600000 -> Identity S----6--R
0x126e00000 - 0x127000000 -> Identity S----6--R
0x26b000000 - 0x26b200000 -> Identity S----6--R
0x2f8000000 - 0x2f8200000 -> Identity S----6--R
0x316800000 - 0x316a00000 -> Identity S----6--R
0x31be00000 - 0x31c000000 -> Identity S----6--R
0x46da00000 - 0x46dc00000 -> Identity S----6--R
(The entire output can be found on gist.)
Conclusion
Shadow stack enhances backward-edge code flow integrity but faces challenges in kernel-mode, where privileged attackers can manipulate page tables. Making SSS pages read-only with SLAT may not be viable protection against such attacks. Supervisor shadow stack restrictions (SSSCheck) on AMD and supervisor shadow-stack control on Intel address these issues. These features allow secure supervisor shadow stack enforcement with minimal performance impact.
Acknowledgement
Thank you Kunal Mehta for providing me feedback on the draft!
I thought of writing this post when he told me about this feature almost two years ago, but I had not because I thought the feature might be a bit too minor. I changed my mind because still little information was available besides the specs, and I had to explain this feature multiple times since then. I would say this feature is more relevant than ever as more hardware supports shadow stack and more platforms depend on it.
Resources
- AMD APM volume 2
- 15.25.14 Supervisor Shadow Stacks
- Intel SDM volume 3
- 26.6.11 Extended-Page-Table Pointer (EPTP)
- 30.3.3.2 EPT Violations
- x86, x64 Instruction Latency, Memory Latency and CPUID dumps
Footnotes
*1 (🔙): Relevant statements from AMD and Intel manuals:
- AMD: “Although is not enforced by the SSS feature, R/W should be 0 in the final nested page table entry in order to achieve the desired security functionality.” (15.25.14)
- Intel: “Clearing bit 1 in the EPT paging-structure entry that maps the page of the guest-physical address does not disallow shadow-stack reads and writes.” (30.3.3.2)
Found this post interesting? We offer a training course about Intel virtualization technology and security. Check out the course syllabus.