Skip to content

Commit fd5439e

Browse files
committed
x86/mm: Check shadow stack page fault errors
The CPU performs "shadow stack accesses" when it expects to encounter shadow stack mappings. These accesses can be implicit (via CALL/RET instructions) or explicit (instructions like WRSS). Shadow stack accesses to shadow-stack mappings can result in faults in normal, valid operation just like regular accesses to regular mappings. Shadow stacks need some of the same features like delayed allocation, swap and copy-on-write. The kernel needs to use faults to implement those features. The architecture has concepts of both shadow stack reads and shadow stack writes. Any shadow stack access to non-shadow stack memory will generate a fault with the shadow stack error code bit set. This means that, unlike normal write protection, the fault handler needs to create a type of memory that can be written to (with instructions that generate shadow stack writes), even to fulfill a read access. So in the case of COW memory, the COW needs to take place even with a shadow stack read. Otherwise the page will be left (shadow stack) writable in userspace. So to trigger the appropriate behavior, set FAULT_FLAG_WRITE for shadow stack accesses, even if the access was a shadow stack read. For the purpose of making this clearer, consider the following example. If a process has a shadow stack, and forks, the shadow stack PTEs will become read-only due to COW. If the CPU in one process performs a shadow stack read access to the shadow stack, for example executing a RET and causing the CPU to read the shadow stack copy of the return address, then in order for the fault to be resolved the PTE will need to be set with shadow stack permissions. But then the memory would be changeable from userspace (from CALL, RET, WRSS, etc). So this scenario needs to trigger COW, otherwise the shared page would be changeable from both processes. Shadow stack accesses can also result in errors, such as when a shadow stack overflows, or if a shadow stack access occurs to a non-shadow-stack mapping. Also, generate the errors for invalid shadow stack accesses. Co-developed-by: Yu-cheng Yu <[email protected]> Signed-off-by: Yu-cheng Yu <[email protected]> Signed-off-by: Rick Edgecombe <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Reviewed-by: Borislav Petkov (AMD) <[email protected]> Reviewed-by: Kees Cook <[email protected]> Acked-by: Mike Rapoport (IBM) <[email protected]> Tested-by: Pengfei Xu <[email protected]> Tested-by: John Allen <[email protected]> Tested-by: Kees Cook <[email protected]> Link: https://lore.kernel.org/all/20230613001108.3040476-16-rick.p.edgecombe%40intel.com
1 parent 54007f8 commit fd5439e

File tree

2 files changed

+24
-0
lines changed

2 files changed

+24
-0
lines changed

arch/x86/include/asm/trap_pf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* bit 3 == 1: use of reserved bit detected
1212
* bit 4 == 1: fault was an instruction fetch
1313
* bit 5 == 1: protection keys block access
14+
* bit 6 == 1: shadow stack access fault
1415
* bit 15 == 1: SGX MMU page-fault
1516
*/
1617
enum x86_pf_error_code {
@@ -20,6 +21,7 @@ enum x86_pf_error_code {
2021
X86_PF_RSVD = 1 << 3,
2122
X86_PF_INSTR = 1 << 4,
2223
X86_PF_PK = 1 << 5,
24+
X86_PF_SHSTK = 1 << 6,
2325
X86_PF_SGX = 1 << 15,
2426
};
2527

arch/x86/mm/fault.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,8 +1112,22 @@ access_error(unsigned long error_code, struct vm_area_struct *vma)
11121112
(error_code & X86_PF_INSTR), foreign))
11131113
return 1;
11141114

1115+
/*
1116+
* Shadow stack accesses (PF_SHSTK=1) are only permitted to
1117+
* shadow stack VMAs. All other accesses result in an error.
1118+
*/
1119+
if (error_code & X86_PF_SHSTK) {
1120+
if (unlikely(!(vma->vm_flags & VM_SHADOW_STACK)))
1121+
return 1;
1122+
if (unlikely(!(vma->vm_flags & VM_WRITE)))
1123+
return 1;
1124+
return 0;
1125+
}
1126+
11151127
if (error_code & X86_PF_WRITE) {
11161128
/* write, present and write, not present: */
1129+
if (unlikely(vma->vm_flags & VM_SHADOW_STACK))
1130+
return 1;
11171131
if (unlikely(!(vma->vm_flags & VM_WRITE)))
11181132
return 1;
11191133
return 0;
@@ -1305,6 +1319,14 @@ void do_user_addr_fault(struct pt_regs *regs,
13051319

13061320
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
13071321

1322+
/*
1323+
* Read-only permissions can not be expressed in shadow stack PTEs.
1324+
* Treat all shadow stack accesses as WRITE faults. This ensures
1325+
* that the MM will prepare everything (e.g., break COW) such that
1326+
* maybe_mkwrite() can create a proper shadow stack PTE.
1327+
*/
1328+
if (error_code & X86_PF_SHSTK)
1329+
flags |= FAULT_FLAG_WRITE;
13081330
if (error_code & X86_PF_WRITE)
13091331
flags |= FAULT_FLAG_WRITE;
13101332
if (error_code & X86_PF_INSTR)

0 commit comments

Comments
 (0)