@@ -0,0 +1,136 @@+.. SPDX-License-Identifier: GPL-2.0++=========================================+Control-flow Enforcement Technology (CET)+=========================================++[1] Overview+============++Control-flow Enforcement Technology (CET) is an Intel processor feature+that provides protection against return/jump-oriented programming (ROP)+attacks. It can be set up to protect both applications and the kernel.+Only user-mode protection is implemented in the 64-bit kernel, including+support for running legacy 32-bit applications.++CET introduces Shadow Stack and Indirect Branch Tracking. Shadow stack is+a secondary stack allocated from memory and cannot be directly modified by+applications. When executing a CALL instruction, the processor pushes the+return address to both the normal stack and the shadow stack. Upon+function return, the processor pops the shadow stack copy and compares it+to the normal stack copy. If the two differ, the processor raises a+control-protection fault. Indirect branch tracking verifies indirect+CALL/JMP targets are intended as marked by the compiler with 'ENDBR'+opcodes.++There are two Kconfig options:++ X86_SHADOW_STACK, and X86_IBT.++To build a CET-enabled kernel, Binutils v2.31 and GCC v8.1 or LLVM v10.0.1+or later are required. To build a CET-enabled application, GLIBC v2.28 or+later is also required.++There are two command-line options for disabling CET features::++ no_user_shstk - disables user shadow stack, and+ no_user_ibt - disables user indirect branch tracking.++At run time, /proc/cpuinfo shows CET features if the processor supports+CET.++[2] Application Enabling+========================++An application's CET capability is marked in its ELF header and can be+verified from readelf/llvm-readelf output:++ readelf -n <application> | grep -a SHSTK+ properties: x86 feature: IBT, SHSTK++If an application supports CET and is statically linked, it will run with+CET protection. If the application needs any shared libraries, the loader+checks all dependencies and enables CET when all requirements are met.++[3] Backward Compatibility+==========================++GLIBC provides a few CET tunables via the GLIBC_TUNABLES environment+variable:++GLIBC_TUNABLES=glibc.tune.hwcaps=-SHSTK,-IBT+ Turn off SHSTK/IBT.++GLIBC_TUNABLES=glibc.tune.x86_shstk=<on, permissive>+ This controls how dlopen() handles SHSTK legacy libraries::++ on - continue with SHSTK enabled;+ permissive - continue with SHSTK off.++Details can be found in the GLIBC manual pages.++[4] CET arch_prctl()'s+======================++Several arch_prctl()'s have been added for CET:++arch_prctl(ARCH_X86_CET_STATUS, u64 *addr)+ Return CET feature status.++ The parameter 'addr' is a pointer to a user buffer.+ On returning to the caller, the kernel fills the following+ information::++ *addr = shadow stack/indirect branch tracking status+ *(addr + 1) = shadow stack base address+ *(addr + 2) = shadow stack size++arch_prctl(ARCH_X86_CET_DISABLE, unsigned int features)+ Disable shadow stack and/or indirect branch tracking as specified in+ 'features'. Return -EPERM if CET is locked.++arch_prctl(ARCH_X86_CET_LOCK)+ Lock in all CET features. They cannot be turned off afterwards.++Note:+ There is no CET-enabling arch_prctl function. By design, CET is enabled+ automatically if the binary and the system can support it.++[5] The implementation of the Shadow Stack+==========================================++Shadow Stack size+-----------------++A task's shadow stack is allocated from memory to a fixed size of+MIN(RLIMIT_STACK, 4 GB). In other words, the shadow stack is allocated to+the maximum size of the normal stack, but capped to 4 GB. However,+a compat-mode application's address space is smaller, each of its thread's+shadow stack size is MIN(1/4 RLIMIT_STACK, 4 GB).++Signal+------++The main program and its signal handlers use the same shadow stack.+Because the shadow stack stores only return addresses, a large shadow+stack covers the condition that both the program stack and the signal+alternate stack run out.++The kernel creates a restore token for the shadow stack restoring address+and verifies that token when restoring from the signal handler.++Fork+----++The shadow stack's vma has VM_SHADOW_STACK flag set; its PTEs are required+to be read-only and dirty. When a shadow stack PTE is not RO and dirty, a+shadow access triggers a page fault with the shadow stack access bit set+in the page fault error code.++When a task forks a child, its shadow stack PTEs are copied and both the+parent's and the child's shadow stack PTEs are cleared of the dirty bit.+Upon the next shadow stack access, the resulting shadow stack page fault+is handled by page copy/re-use.++When a pthread child is created, the kernel allocates a new shadow stack+for the new thread.
Shadow Stack provides protection against function return address
corruption. It is active when the processor supports it, the kernel has
CONFIG_X86_SHADOW_STACK enabled, and the application is built for the
feature. This is only implemented for the 64-bit kernel. When it is
enabled, legacy non-Shadow Stack applications continue to work, but without
protection.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
---
v24:
- Update for the splitting X86_CET to X86_SHADOW_STACK and X86_IBT.
arch/x86/Kconfig | 26 ++++++++++++++++++++++++++
arch/x86/Kconfig.assembler | 5 +++++
2 files changed, 31 insertions(+)
Control-flow Enforcement Technology (CET) introduces these MSRs:
MSR_IA32_U_CET (user-mode CET settings),
MSR_IA32_PL3_SSP (user-mode shadow stack pointer),
MSR_IA32_PL0_SSP (kernel-mode shadow stack pointer),
MSR_IA32_PL1_SSP (Privilege Level 1 shadow stack pointer),
MSR_IA32_PL2_SSP (Privilege Level 2 shadow stack pointer),
MSR_IA32_S_CET (kernel-mode CET settings),
MSR_IA32_INT_SSP_TAB (exception shadow stack table).
The two user-mode MSRs belong to XFEATURE_CET_USER. The first three of
kernel-mode MSRs belong to XFEATURE_CET_KERNEL. Both XSAVES states are
supervisor states. This means that there is no direct, unprivileged access
to these states, making it harder for an attacker to subvert CET.
For sigreturn and future ptrace() support, shadow stack address and MSR
reserved bits are checked before written to the supervisor states.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
---
arch/x86/include/asm/fpu/types.h | 23 +++++++++++++++++++++--
arch/x86/include/asm/fpu/xstate.h | 6 ++++--
arch/x86/include/asm/msr-index.h | 19 +++++++++++++++++++
arch/x86/kernel/fpu/xstate.c | 10 +++++++++-
4 files changed, 53 insertions(+), 5 deletions(-)
@@ -35,7 +35,8 @@XFEATURE_MASK_BNDCSR)/* All currently supported supervisor features */-#define XFEATURE_MASK_SUPERVISOR_SUPPORTED (XFEATURE_MASK_PASID)+#define XFEATURE_MASK_SUPERVISOR_SUPPORTED (XFEATURE_MASK_PASID | \+XFEATURE_MASK_CET_USER)/**Asupervisorstatecomponentmaynotalwayscontainvaluableinformation,
@@ -62,7 +63,8 @@*Unsupportedsupervisorfeatures.Whenasupervisorfeatureinthismaskis*supportedinthefuture,moveittothesupportedsupervisorfeaturemask.*/-#define XFEATURE_MASK_SUPERVISOR_UNSUPPORTED (XFEATURE_MASK_PT)+#define XFEATURE_MASK_SUPERVISOR_UNSUPPORTED (XFEATURE_MASK_PT | \+XFEATURE_MASK_CET_KERNEL)/* All supervisor states including supported and unsupported states. */#define XFEATURE_MASK_SUPERVISOR_ALL (XFEATURE_MASK_SUPERVISOR_SUPPORTED | \
Shadow stack accesses are those that are performed by the CPU where it
expects to encounter a shadow stack mapping. These accesses are performed
implicitly by CALL/RET at the site of the shadow stack pointer. These
accesses are made explicitly by shadow stack management instructions like
WRUSSQ.
Shadow stacks accesses to shadow-stack mapping can see 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.
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.
In handling a shadow stack page fault, verify it occurs within a shadow
stack mapping. It is always an error otherwise. For valid shadow stack
accesses, set FAULT_FLAG_WRITE to effect copy-on-write. Because clearing
_PAGE_DIRTY (vs. _PAGE_RW) is used to trigger the fault, shadow stack read
fault and shadow stack write fault are not differentiated and both are
handled as a write access.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
---
v24:
- Change VM_SHSTK to VM_SHADOW_STACK.
arch/x86/include/asm/trap_pf.h | 2 ++
arch/x86/mm/fault.c | 19 +++++++++++++++++++
2 files changed, 21 insertions(+)
A control-protection fault is triggered when a control-flow transfer
attempt violates Shadow Stack or Indirect Branch Tracking constraints.
For example, the return address for a RET instruction differs from the copy
on the shadow stack; or an indirect JMP instruction, without the NOTRACK
prefix, arrives at a non-ENDBR opcode.
The control-protection fault handler works in a similar way as the general
protection fault handler. It provides the si_code SEGV_CPERR to the signal
handler.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
Cc: Michael Kerrisk <redacted>
---
arch/x86/include/asm/idtentry.h | 4 ++
arch/x86/kernel/idt.c | 4 ++
arch/x86/kernel/signal_compat.c | 2 +-
arch/x86/kernel/traps.c | 63 ++++++++++++++++++++++++++++++
include/uapi/asm-generic/siginfo.h | 3 +-
5 files changed, 74 insertions(+), 2 deletions(-)
@@ -571,6 +571,10 @@ DECLARE_IDTENTRY_ERRORCODE(X86_TRAP_SS, exc_stack_segment);DECLARE_IDTENTRY_ERRORCODE(X86_TRAP_GP,exc_general_protection);DECLARE_IDTENTRY_ERRORCODE(X86_TRAP_AC,exc_alignment_check);+#ifdef CONFIG_X86_CET+DECLARE_IDTENTRY_ERRORCODE(X86_TRAP_CP,exc_control_protection);+#endif+/* Raw exception entries which need extra work */DECLARE_IDTENTRY_RAW(X86_TRAP_UD,exc_invalid_op);DECLARE_IDTENTRY_RAW(X86_TRAP_BP,exc_int3);
@@ -606,6 +607,68 @@ DEFINE_IDTENTRY_ERRORCODE(exc_general_protection)cond_local_irq_disable(regs);}+#ifdef CONFIG_X86_CET+staticconstchar*constcontrol_protection_err[]={+"unknown",+"near-ret",+"far-ret/iret",+"endbranch",+"rstorssp",+"setssbsy",+"unknown",+};++staticDEFINE_RATELIMIT_STATE(cpf_rate,DEFAULT_RATELIMIT_INTERVAL,+DEFAULT_RATELIMIT_BURST);++/*+*Whenacontrolprotectionexceptionoccurs,sendasignaltotheresponsible+*application.Currently,controlprotectionisonlyenabledforusermode.+*Thisexceptionshouldnotcomefromkernelmode.+*/+DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)+{+structtask_struct*tsk;++if(!user_mode(regs)){+pr_emerg("PANIC: unexpected kernel control protection fault\n");+die("kernel control protection fault",regs,error_code);+panic("Machine halted.");+}++cond_local_irq_enable(regs);++if(!boot_cpu_has(X86_FEATURE_CET))+WARN_ONCE(1,"Control protection fault with CET support disabled\n");++tsk=current;+tsk->thread.error_code=error_code;+tsk->thread.trap_nr=X86_TRAP_CP;++/*+*Ratelimittopreventlogspamming.+*/+if(show_unhandled_signals&&unhandled_signal(tsk,SIGSEGV)&&+__ratelimit(&cpf_rate)){+unsignedlongssp;+intcpf_type;++cpf_type=array_index_nospec(error_code,ARRAY_SIZE(control_protection_err));++rdmsrl(MSR_IA32_PL3_SSP,ssp);+pr_emerg("%s[%d] control protection ip:%lx sp:%lx ssp:%lx error:%lx(%s)",+tsk->comm,task_pid_nr(tsk),+regs->ip,regs->sp,ssp,error_code,+control_protection_err[cpf_type]);+print_vma_addr(KERN_CONT" in ",regs->ip);+pr_cont("\n");+}++force_sig_fault(SIGSEGV,SEGV_CPERR,(void__user*)0);+cond_local_irq_disable(regs);+}+#endif+staticbooldo_int3(structpt_regs*regs){intres;
The x86 family of processors do not directly create read-only and Dirty
PTEs. These PTEs are created by software. One such case is that kernel
read-only pages are historically setup as Dirty.
New processors that support Shadow Stack regard read-only and Dirty PTEs as
shadow stack pages. This results in ambiguity between shadow stack and
kernel read-only pages. To resolve this, removed Dirty from kernel read-
only pages.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kirill A. Shutemov <redacted>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Kees Cook <redacted>
Cc: Thomas Gleixner <redacted>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Peter Zijlstra <peterz@infradead.org>
---
arch/x86/include/asm/pgtable_types.h | 6 +++---
arch/x86/mm/pat/set_memory.c | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
@@ -1932,7 +1932,7 @@ int set_memory_nx(unsigned long addr, int numpages)intset_memory_ro(unsignedlongaddr,intnumpages){-returnchange_page_attr_clear(&addr,numpages,__pgprot(_PAGE_RW),0);+returnchange_page_attr_clear(&addr,numpages,__pgprot(_PAGE_RW|_PAGE_DIRTY),0);}intset_memory_rw(unsignedlongaddr,intnumpages)
To prepare the introduction of _PAGE_COW, move pmd_write() and
pud_write() up in the file, so that they can be used by other
helpers below.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kirill A. Shutemov <redacted>
---
arch/x86/include/asm/pgtable.h | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
There is essentially no room left in the x86 hardware PTEs on some OSes
(not Linux). That left the hardware architects looking for a way to
represent a new memory type (shadow stack) within the existing bits.
They chose to repurpose a lightly-used state: Write=0, Dirty=1.
The reason it's lightly used is that Dirty=1 is normally set by hardware
and cannot normally be set by hardware on a Write=0 PTE. Software must
normally be involved to create one of these PTEs, so software can simply
opt to not create them.
In places where Linux normally creates Write=0, Dirty=1, it can use the
software-defined _PAGE_COW in place of the hardware _PAGE_DIRTY. In other
words, whenever Linux needs to create Write=0, Dirty=1, it instead creates
Write=0, Cow=1, except for shadow stack, which is Write=0, Dirty=1. This
clearly separates shadow stack from other data, and results in the
following:
(a) A modified, copy-on-write (COW) page: (Write=0, Cow=1)
(b) A R/O page that has been COW'ed: (Write=0, Cow=1)
The user page is in a R/O VMA, and get_user_pages() needs a writable
copy. The page fault handler creates a copy of the page and sets
the new copy's PTE as Write=0 and Cow=1.
(c) A shadow stack PTE: (Write=0, Dirty=1)
(d) A shared shadow stack PTE: (Write=0, Cow=1)
When a shadow stack page is being shared among processes (this happens
at fork()), its PTE is made Dirty=0, so the next shadow stack access
causes a fault, and the page is duplicated and Dirty=1 is set again.
This is the COW equivalent for shadow stack pages, even though it's
copy-on-access rather than copy-on-write.
(e) A page where the processor observed a Write=1 PTE, started a write, set
Dirty=1, but then observed a Write=0 PTE. That's possible today, but
will not happen on processors that support shadow stack.
Define _PAGE_COW and update pte_*() helpers and apply the same changes to
pmd and pud.
After this, there are six free bits left in the 64-bit PTE, and no more
free bits in the 32-bit PTE (except for PAE) and Shadow Stack is not
implemented for the 32-bit kernel.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kirill A. Shutemov <redacted>
---
v24:
- Replace CONFIG_X86_CET with CONFIG_X86_SHADOW_STACK to reflect the Kconfig
changes.
arch/x86/include/asm/pgtable.h | 195 ++++++++++++++++++++++++---
arch/x86/include/asm/pgtable_types.h | 42 +++++-
2 files changed, 216 insertions(+), 21 deletions(-)
@@ -23,7 +23,8 @@#define _PAGE_BIT_SOFTW2 10 /* " */#define _PAGE_BIT_SOFTW3 11 /* " */#define _PAGE_BIT_PAT_LARGE 12 /* On 2MB or 1GB pages */-#define _PAGE_BIT_SOFTW4 58 /* available for programmer */+#define _PAGE_BIT_SOFTW4 57 /* available for programmer */+#define _PAGE_BIT_SOFTW5 58 /* available for programmer */#define _PAGE_BIT_PKEY_BIT0 59 /* Protection Keys, bit 1/4 */#define _PAGE_BIT_PKEY_BIT1 60 /* Protection Keys, bit 2/4 */#define _PAGE_BIT_PKEY_BIT2 61 /* Protection Keys, bit 3/4 */
@@ -36,6 +37,15 @@#define _PAGE_BIT_SOFT_DIRTY _PAGE_BIT_SOFTW3 /* software dirty tracking */#define _PAGE_BIT_DEVMAP _PAGE_BIT_SOFTW4+/*+*Indicatesacopy-on-writepage.+*/+#ifdef CONFIG_X86_SHADOW_STACK+#define _PAGE_BIT_COW _PAGE_BIT_SOFTW5 /* copy-on-write */+#else+#define _PAGE_BIT_COW 0+#endif+/* If _PAGE_BIT_PRESENT is clear, we use these: *//* - if the user mapped it with PROT_NONE; pte_present gives true */#define _PAGE_BIT_PROTNONE _PAGE_BIT_GLOBAL
After the introduction of _PAGE_COW, a modified page's PTE can have either
_PAGE_DIRTY or _PAGE_COW. Change _PAGE_DIRTY to _PAGE_DIRTY_BITS.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
Reviewed-by: Kirill A. Shutemov <redacted>
Cc: David Airlie <redacted>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Jani Nikula <jani.nikula@linux.intel.com>
Cc: Daniel Vetter <redacted>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: Zhenyu Wang <redacted>
Cc: Zhi Wang <redacted>
---
drivers/gpu/drm/i915/gvt/gtt.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
A shadow stack PTE must be read-only and have _PAGE_DIRTY set. However,
read-only and Dirty PTEs also exist for copy-on-write (COW) pages. These
two cases are handled differently for page faults. Introduce
VM_SHADOW_STACK to track shadow stack VMAs.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
---
v24:
- Change VM_SHSTK to VM_SHADOW_STACK.
- Change CONFIG_X86_CET to CONFIG_X86_SHADOW_STACK to reflect Kconfig changes.
Documentation/filesystems/proc.rst | 1 +
arch/x86/mm/mmap.c | 2 ++
fs/proc/task_mmu.c | 3 +++
include/linux/mm.h | 8 ++++++++
4 files changed, 14 insertions(+)
@@ -549,6 +549,7 @@ encoded manner. The codes are the following: mg mergable advise flag bt arm64 BTI guarded page mt arm64 MTE allocation tags are enabled+ ss shadow stack page == ======================================= Note that there is no guarantee that every flag and associated mnemonic will
When Shadow Stack is introduced, [R/O + _PAGE_DIRTY] PTE is reserved for
shadow stack. Copy-on-write PTEs have [R/O + _PAGE_COW].
When a PTE goes from [R/W + _PAGE_DIRTY] to [R/O + _PAGE_COW], it could
become a transient shadow stack PTE in two cases:
The first case is that some processors can start a write but end up seeing
a read-only PTE by the time they get to the Dirty bit, creating a transient
shadow stack PTE. However, this will not occur on processors supporting
Shadow Stack, and a TLB flush is not necessary.
The second case is that when _PAGE_DIRTY is replaced with _PAGE_COW non-
atomically, a transient shadow stack PTE can be created as a result.
Thus, prevent that with cmpxchg.
Dave Hansen, Jann Horn, Andy Lutomirski, and Peter Zijlstra provided many
insights to the issue. Jann Horn provided the cmpxchg solution.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
---
arch/x86/include/asm/pgtable.h | 36 ++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
The read-only and Dirty PTE has been used to indicate copy-on-write pages.
However, newer x86 processors also regard a read-only and Dirty PTE as a
shadow stack page. In order to separate the two, the software-defined
_PAGE_COW is created to replace _PAGE_DIRTY for the copy-on-write case, and
pte_*() are updated.
Pte_modify() changes a PTE to 'newprot', but it doesn't use the pte_*().
Introduce fixup_dirty_pte(), which sets a dirty PTE, based on _PAGE_RW,
to either _PAGE_DIRTY or _PAGE_COW.
Apply the same changes to pmd_modify().
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kirill A. Shutemov <redacted>
---
arch/x86/include/asm/pgtable.h | 37 ++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
When serving a page fault, maybe_mkwrite() makes a PTE writable if its vma
has VM_WRITE.
A shadow stack vma has VM_SHADOW_STACK. Its PTEs have _PAGE_DIRTY, but not
_PAGE_WRITE. In fork(), _PAGE_DIRTY is cleared to cause copy-on-write,
and in the page fault handler, _PAGE_DIRTY is restored and the shadow stack
page is writable again.
Introduce an x86 version of maybe_mkwrite(), which sets proper PTE bits
according to VM flags.
Apply the same changes to maybe_pmd_mkwrite().
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
---
v24:
- Instead of doing arch_maybe_mkwrite(), overwrite maybe*_mkwrite() with x86
versions.
- Change VM_SHSTK to VM_SHADOW_STACK.
arch/x86/include/asm/pgtable.h | 8 ++++++++
arch/x86/mm/pgtable.c | 20 ++++++++++++++++++++
include/linux/mm.h | 2 ++
mm/huge_memory.c | 2 ++
4 files changed, 32 insertions(+)
INCSSP(Q/D) increments shadow stack pointer and 'pops and discards' the
first and the last elements in the range, effectively touches those memory
areas.
The maximum moving distance by INCSSPQ is 255 * 8 = 2040 bytes and
255 * 4 = 1020 bytes by INCSSPD. Both ranges are far from PAGE_SIZE.
Thus, putting a gap page on both ends of a shadow stack prevents INCSSP,
CALL, and RET from going beyond.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
---
v24:
- Instead changing vm_*_gap(), create x86-specific versions.
arch/x86/include/asm/page_types.h | 17 +++++++++++++++
arch/x86/mm/mmap.c | 36 +++++++++++++++++++++++++++++++
include/linux/mm.h | 4 ++++
3 files changed, 57 insertions(+)
When serving a page fault, maybe_mkwrite() makes a PTE writable if it is in
a writable vma. A shadow stack vma is writable, but its PTEs need
_PAGE_DIRTY to be set to become writable. For this reason, maybe_mkwrite()
has been updated.
There are a few places that call pte_mkwrite() directly, but have the
same result as from maybe_mkwrite(). These sites need to be updated for
shadow stack as well. Thus, change them to maybe_mkwrite():
- do_anonymous_page() and migrate_vma_insert_page() check VM_WRITE directly
and call pte_mkwrite(), which is the same as maybe_mkwrite(). Change
them to maybe_mkwrite().
- In do_numa_page(), if the numa entry was writable, then pte_mkwrite()
is called directly. Fix it by doing maybe_mkwrite().
- In change_pte_range(), pte_mkwrite() is called directly. Replace it with
maybe_mkwrite().
A shadow stack vma is writable but has different vma
flags, and handled accordingly in maybe_mkwrite().
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
---
mm/memory.c | 5 ++---
mm/migrate.c | 3 +--
mm/mprotect.c | 2 +-
3 files changed, 4 insertions(+), 6 deletions(-)
Can_follow_write_pte() ensures a read-only page is COWed by checking the
FOLL_COW flag, and uses pte_dirty() to validate the flag is still valid.
Like a writable data page, a shadow stack page is writable, and becomes
read-only during copy-on-write, but it is always dirty. Thus, in the
can_follow_write_pte() check, it belongs to the writable page case and
should be excluded from the read-only page pte_dirty() check. Apply
the same changes to can_follow_write_pmd().
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
---
v24:
- Change arch_shadow_stack_mapping() to is_shadow_stack_mapping().
mm/gup.c | 8 +++++---
mm/huge_memory.c | 8 +++++---
2 files changed, 10 insertions(+), 6 deletions(-)
In change_pte_range(), when a PTE is changed for prot_numa, _PAGE_RW is
preserved to avoid the additional write fault after the NUMA hinting fault.
However, pte_write() now includes both normal writable and shadow stack
(RW=0, Dirty=1) PTEs, but the latter does not have _PAGE_RW and has no need
to preserve it.
Exclude shadow stack from preserve_write test, and apply the same change to
change_huge_pmd().
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kirill A. Shutemov <redacted>
---
v24:
- Change arch_shadow_stack_mapping() to is_shadow_stack_mapping().
mm/huge_memory.c | 7 ++++++-
mm/mprotect.c | 9 ++++++++-
2 files changed, 14 insertions(+), 2 deletions(-)
@@ -1071,6 +1071,7 @@ unsigned long do_mmap(struct file *file,unsignedlonglen,unsignedlongprot,unsignedlongflags,+vm_flags_tvm_flags,unsignedlongpgoff,unsignedlong*populate,structlist_head*uf)
@@ -1078,7 +1079,6 @@ unsigned long do_mmap(struct file *file,structvm_area_struct*vma;structvm_region*region;structrb_node*rb;-vm_flags_tvm_flags;unsignedlongcapabilities,result;intret;
@@ -1097,7 +1097,7 @@ unsigned long do_mmap(struct file *file,/* we've determined that we can make the mapping, now translate what we*nowknowintoVMAflags*/-vm_flags=determine_vm_flags(file,prot,flags,capabilities);+vm_flags|=determine_vm_flags(file,prot,flags,capabilities);/* we're going to need to record the mapping */region=kmem_cache_zalloc(vm_region_jar,GFP_KERNEL);
@@ -535,6 +536,10 @@ struct thread_struct {unsignedintsig_on_uaccess_err:1;+#ifdef CONFIG_X86_CET+structcet_statuscet;+#endif+/* Floating point and extended processor state */structfpufpu;/*
The kernel allocates (and frees on thread exit) a new shadow stack for a
pthread child.
It is possible for the kernel to complete the clone syscall and set the
child's shadow stack pointer to NULL and let the child thread allocate
a shadow stack for itself. There are two issues in this approach: It
is not compatible with existing code that does inline syscall and it
cannot handle signals before the child can successfully allocate a
shadow stack.
Use stack_size passed from clone3() syscall for thread shadow stack size,
but cap it to min(RLIMIT_STACK, 4 GB). A compat-mode thread shadow stack
size is further reduced to 1/4. This allows more threads to run in a 32-
bit address space.
Signed-off-by: Yu-cheng Yu <redacted>
---
arch/x86/include/asm/cet.h | 5 +++
arch/x86/include/asm/mmu_context.h | 3 ++
arch/x86/kernel/process.c | 15 ++++++--
arch/x86/kernel/shstk.c | 57 +++++++++++++++++++++++++++++-
4 files changed, 76 insertions(+), 4 deletions(-)
@@ -122,8 +124,9 @@ static int set_new_tls(struct task_struct *p, unsigned long tls)returndo_set_thread_area_64(p,ARCH_SET_FS,tls);}-intcopy_thread(unsignedlongclone_flags,unsignedlongsp,unsignedlongarg,-structtask_struct*p,unsignedlongtls)+intcopy_thread(unsignedlongclone_flags,unsignedlongsp,+unsignedlongstack_size,structtask_struct*p,+unsignedlongtls){structinactive_task_frame*frame;structfork_frame*fork_frame;
@@ -163,7 +166,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, unsigned long arg,/* Kernel thread ? */if(unlikely(p->flags&(PF_KTHREAD|PF_IO_WORKER))){memset(childregs,0,sizeof(structpt_regs));-kthread_frame_init(frame,sp,arg);+kthread_frame_init(frame,sp,stack_size);return0;}
@@ -181,6 +184,12 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, unsigned long arg,if(clone_flags&CLONE_SETTLS)ret=set_new_tls(p,tls);+#ifdef CONFIG_X86_64+/* Allocate a new shadow stack for pthread */+if(!ret)+ret=shstk_setup_thread(p,clone_flags,stack_size);+#endif+if(!ret&&unlikely(test_tsk_thread_flag(current,TIF_IO_BITMAP)))io_bitmap_share(p);
@@ -75,6 +75,55 @@ int shstk_setup(void)return0;}+intshstk_setup_thread(structtask_struct*tsk,unsignedlongclone_flags,+unsignedlongstack_size)+{+unsignedlongaddr,size;+structcet_user_state*state;+structcet_status*cet=&tsk->thread.cet;++if(!cet->shstk_size)+return0;++if((clone_flags&(CLONE_VFORK|CLONE_VM))!=CLONE_VM)+return0;++state=get_xsave_addr(&tsk->thread.fpu.state.xsave,+XFEATURE_CET_USER);++if(!state)+return-EINVAL;++if(stack_size==0)+return-EINVAL;++/* Cap shadow stack size to 4 GB */+size=min_t(unsignedlonglong,rlimit(RLIMIT_STACK),SZ_4G);+size=min(size,stack_size);++/*+*Compat-modepthreadssharealimitedaddressspace.+*Ifeachfunctioncalltakesanaverageoffourslots+*stackspace,allocate1/4ofstacksizeforshadowstack.+*/+if(in_compat_syscall())+size/=4;+size=round_up(size,PAGE_SIZE);+addr=alloc_shstk(size,0);++if(IS_ERR_VALUE(addr)){+cet->shstk_base=0;+cet->shstk_size=0;+returnPTR_ERR((void*)addr);+}++fpu__prepare_write(&tsk->thread.fpu);+state->user_ssp=(u64)(addr+size);+cet->shstk_base=addr;+cet->shstk_size=size;+return0;+}+voidshstk_free(structtask_struct*tsk){structcet_status*cet=&tsk->thread.cet;
A shadow stack restore token marks a restore point of the shadow stack, and
the address in a token must point directly above the token, which is within
the same shadow stack. This is distinctively different from other pointers
on the shadow stack, since those pointers point to executable code area.
The restore token can be used as an extra protection for signal handling.
To deliver a signal, create a shadow stack restore token and put the token
and the signal restorer address on the shadow stack. In sigreturn, verify
the token and restore from it the shadow stack pointer.
Introduce token setup and verify routines. Also introduce WRUSS, which is
a kernel-mode instruction but writes directly to user shadow stack. It is
used to construct user signal stack as described above.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
---
arch/x86/include/asm/cet.h | 9 ++
arch/x86/include/asm/special_insns.h | 32 +++++++
arch/x86/kernel/shstk.c | 126 +++++++++++++++++++++++++++
3 files changed, 167 insertions(+)
When shadow stack is enabled, a task's shadow stack states must be saved
along with the signal context and later restored in sigreturn. However,
currently there is no systematic facility for extending a signal context.
Introduce a signal context extension struct 'sc_ext', which is used to save
shadow stack restore token address and WAIT_ENDBR status[1]. The extension
is located above the fpu states, plus alignment.
Introduce routines for the allocation, save, and restore for sc_ext:
- fpu__alloc_sigcontext_ext(),
- save_extra_state_to_sigframe(),
- get_extra_state_from_sigframe(),
- restore_extra_state().
[1] WAIT_ENDBR will be introduced later in the Indirect Branch Tracking
series, but add that into sc_ext now to keep the struct stable in case
the IBT series is applied later.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
---
v24:
- Split out shadow stack token routines to a separate patch.
- Put signal frame save/restore routines to fpu/signal.c and re-name accordingly.
arch/x86/ia32/ia32_signal.c | 16 +++
arch/x86/include/asm/cet.h | 2 +
arch/x86/include/asm/fpu/internal.h | 2 +
arch/x86/include/uapi/asm/sigcontext.h | 9 ++
arch/x86/kernel/fpu/signal.c | 143 +++++++++++++++++++++++++
arch/x86/kernel/signal.c | 9 ++
6 files changed, 181 insertions(+)
@@ -196,6 +196,15 @@ struct _xstate {/* New processor state extensions go here: */};+/*+*Locatedattheendofsigcontext->fpstate,alignedto8.+*/+structsc_ext{+unsignedlongtotal_size;+unsignedlongssp;+unsignedlongwait_endbr;+};+/**The32-bitsignalframe:*/
An ELF file's .note.gnu.property indicates arch features supported by the
file. These features are extracted by arch_parse_elf_property() and stored
in 'arch_elf_state'.
Introduce x86 feature definitions and arch_setup_elf_property(), which
enables such features. The first use-case of this function is Shadow
Stack.
ARM64 is the other arch that has ARCH_USE_GNU_PROPERTY and arch_parse_elf_
property(). Add arch_setup_elf_property() for it.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
Cc: Mark Brown <broonie@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Dave Martin <Dave.Martin@arm.com>
---
v24:
- Change cet_setup_shstk() to shstk_setup() to reflect function name changes
relating to the splitting of shadow stack and ibt.
arch/arm64/include/asm/elf.h | 5 +++++
arch/x86/Kconfig | 2 ++
arch/x86/include/asm/elf.h | 13 +++++++++++++
arch/x86/kernel/process_64.c | 32 ++++++++++++++++++++++++++++++++
fs/binfmt_elf.c | 4 ++++
include/linux/elf.h | 6 ++++++
include/uapi/linux/elf.h | 9 +++++++++
7 files changed, 71 insertions(+)
arch_prctl(ARCH_X86_CET_STATUS, u64 *args)
Get CET feature status.
The parameter 'args' is a pointer to a user buffer. The kernel returns
the following information:
*args = shadow stack/IBT status
*(args + 1) = shadow stack base address
*(args + 2) = shadow stack size
32-bit binaries use the same interface, but only lower 32-bits of each
item.
arch_prctl(ARCH_X86_CET_DISABLE, unsigned int features)
Disable CET features specified in 'features'. Return -EPERM if CET is
locked.
arch_prctl(ARCH_X86_CET_LOCK)
Lock in CET features.
Also change do_arch_prctl_common()'s parameter 'cpuid_enabled' to
'arg2', as it is now also passed to prctl_cet().
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
---
v24:
- Update #ifdef placement relating to shadow stack and ibt split.
- Update function names.
arch/x86/include/asm/cet.h | 7 ++++
arch/x86/include/uapi/asm/prctl.h | 4 +++
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/cet_prctl.c | 60 +++++++++++++++++++++++++++++++
arch/x86/kernel/process.c | 6 ++--
5 files changed, 75 insertions(+), 3 deletions(-)
create mode 100644 arch/x86/kernel/cet_prctl.c
When newer VM flags are being created, such as VM_MTE, it becomes necessary
for mmap/mprotect to verify if certain flags are being applied to an
anonymous VMA.
To solve this, one approach is adding a VM flag to track that MAP_ANONYMOUS
is specified [1], and then using the flag in arch_validate_flags().
Another approach is passing vma_is_anonymous() to arch_validate_flags().
To prepare the introduction of PROT_SHSTK, which creates a shadow stack
mapping and can only be applied to an anonymous VMA, update arch_validate_
flags() to include anonymous VMA information.
[1] commit 9f3419315f3c ("arm64: mte: Add PROT_MTE support to mmap() and mprotect()"),
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
Cc: Vincenzo Frascino <vincenzo.frascino@arm.com>
Cc: Will Deacon <will@kernel.org>
---
arch/arm64/include/asm/mman.h | 4 ++--
arch/sparc/include/asm/mman.h | 4 ++--
include/linux/mman.h | 2 +-
mm/mmap.c | 2 +-
mm/mprotect.c | 2 +-
5 files changed, 7 insertions(+), 7 deletions(-)
@@ -82,6 +82,6 @@ static inline bool arch_validate_flags(unsigned long vm_flags)/* only allow VM_MTE if VM_MTE_ALLOWED has been set previously */return!(vm_flags&VM_MTE)||(vm_flags&VM_MTE_ALLOWED);}-#define arch_validate_flags(vm_flags) arch_validate_flags(vm_flags)+#define arch_validate_flags(vm_flags, is_anon) arch_validate_flags(vm_flags, is_anon)#endif /* ! __ASM_MMAN_H__ */
@@ -60,11 +60,11 @@ static inline int sparc_validate_prot(unsigned long prot, unsigned long addr)return1;}-#define arch_validate_flags(vm_flags) arch_validate_flags(vm_flags)+#define arch_validate_flags(vm_flags, is_anon) arch_validate_flags(vm_flags, is_anon)/* arch_validate_flags() - Ensure combination of flags is valid for a*VMA.*/-staticinlineboolarch_validate_flags(unsignedlongvm_flags)+staticinlineboolarch_validate_flags(unsignedlongvm_flags,boolis_anon){/* If ADI is being enabled on this VMA, check for ADI*capabilityontheplatformandensureVMAissuitable
@@ -1850,7 +1850,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,}/* Allow architectures to sanity-check the vm_flags */-if(!arch_validate_flags(vma->vm_flags)){+if(!arch_validate_flags(vma->vm_flags,vma_is_anonymous(vma))){error=-EINVAL;if(file)gotounmap_and_free_vma;
@@ -611,7 +611,7 @@ static int do_mprotect_pkey(unsigned long start, size_t len,}/* Allow architectures to sanity-check the new flags */-if(!arch_validate_flags(newflags)){+if(!arch_validate_flags(newflags,vma_is_anonymous(vma))){error=-EINVAL;gotoout;}
There are three possible options to create a shadow stack allocation API:
an arch_prctl, a new syscall, or adding PROT_SHSTK to mmap()/mprotect().
Each has its advantages and compromises.
An arch_prctl() is the least intrusive. However, the existing x86
arch_prctl() takes only two parameters. Multiple parameters must be
passed in a memory buffer. There is a proposal to pass more parameters in
registers [1], but no active discussion on that.
A new syscall minimizes compatibility issues and offers an extensible frame
work to other architectures, but this will likely result in some overlap of
mmap()/mprotect().
The introduction of PROT_SHSTK to mmap()/mprotect() takes advantage of
existing APIs. The x86-specific PROT_SHSTK is translated to
VM_SHADOW_STACK and a shadow stack mapping is created without reinventing
the wheel. There are potential pitfalls though. The most obvious one
would be using this as a bypass to shadow stack protection. However, the
attacker would have to get to the syscall first.
[1] https://lore.kernel.org/lkml/20200828121624.108243-1-hjl.tools@gmail.com/
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
---
v24:
- Update arch_calc_vm_prot_bits(), leave PROT* checking to
arch_validate_prot().
- Update arch_validate_prot(), leave vma flags checking to
arch_validate_flags().
- Add arch_validate_flags().
arch/x86/include/asm/mman.h | 59 +++++++++++++++++++++++++++++++-
arch/x86/include/uapi/asm/mman.h | 1 +
include/linux/mm.h | 1 +
3 files changed, 60 insertions(+), 1 deletion(-)
From: Andy Lutomirski <luto@kernel.org> Date: 2021-04-06 22:49:49
On Thu, Apr 1, 2021 at 3:12 PM Yu-cheng Yu [off-list ref] wrote:
quoted hunk
A shadow stack restore token marks a restore point of the shadow stack, and
the address in a token must point directly above the token, which is within
the same shadow stack. This is distinctively different from other pointers
on the shadow stack, since those pointers point to executable code area.
The restore token can be used as an extra protection for signal handling.
To deliver a signal, create a shadow stack restore token and put the token
and the signal restorer address on the shadow stack. In sigreturn, verify
the token and restore from it the shadow stack pointer.
Introduce token setup and verify routines. Also introduce WRUSS, which is
a kernel-mode instruction but writes directly to user shadow stack. It is
used to construct user signal stack as described above.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
---
arch/x86/include/asm/cet.h | 9 ++
arch/x86/include/asm/special_insns.h | 32 +++++++
arch/x86/kernel/shstk.c | 126 +++++++++++++++++++++++++++
3 files changed, 167 insertions(+)
+}
+#else
+static inline int write_user_shstk_32(unsigned long addr, unsigned int val)
+{
+ WARN_ONCE(1, "%s used but not supported.\n", __func__);
+ return -EFAULT;
+}
+#endif
+
+static inline int write_user_shstk_64(unsigned long addr, unsigned long val)
From: Andy Lutomirski <luto@kernel.org> Date: 2021-04-06 22:51:06
On Thu, Apr 1, 2021 at 3:11 PM Yu-cheng Yu [off-list ref] wrote:
When shadow stack is enabled, a task's shadow stack states must be saved
along with the signal context and later restored in sigreturn. However,
currently there is no systematic facility for extending a signal context.
Introduce a signal context extension struct 'sc_ext', which is used to save
shadow stack restore token address and WAIT_ENDBR status[1]. The extension
is located above the fpu states, plus alignment.
Introduce routines for the allocation, save, and restore for sc_ext:
- fpu__alloc_sigcontext_ext(),
- save_extra_state_to_sigframe(),
- get_extra_state_from_sigframe(),
- restore_extra_state().
[1] WAIT_ENDBR will be introduced later in the Indirect Branch Tracking
series, but add that into sc_ext now to keep the struct stable in case
the IBT series is applied later.
Please don't. Instead, please figure out how that structure gets
extended for real, and organize your patches to demonstrate that the
extension works.
quoted hunk
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
---
v24:
- Split out shadow stack token routines to a separate patch.
- Put signal frame save/restore routines to fpu/signal.c and re-name accordingly.
arch/x86/ia32/ia32_signal.c | 16 +++
arch/x86/include/asm/cet.h | 2 +
arch/x86/include/asm/fpu/internal.h | 2 +
arch/x86/include/uapi/asm/sigcontext.h | 9 ++
arch/x86/kernel/fpu/signal.c | 143 +++++++++++++++++++++++++
arch/x86/kernel/signal.c | 9 ++
6 files changed, 181 insertions(+)
Why do we need another copy of this logic? You're trying to push the
correct return address for the signal handler function onto the stack.
Please calculate that return address once and then use it here.
@@ -196,6 +196,15 @@ struct _xstate {/* New processor state extensions go here: */};+/*+*Locatedattheendofsigcontext->fpstate,alignedto8.+*/+structsc_ext{+unsignedlongtotal_size;+unsignedlongssp;+unsignedlongwait_endbr;+};
We need some proper documentation and an extensibility story for this.
This won't be the last time we extend the signal state. Keep in mind
that the FPU state is very likely to become genuinely variable sized
due to AVX-512 and AMX.
We also have the ability to extend ucontext, I believe, and I'd like
some analysis of why we want to put ssp and wait_endbr into the FPU
context instead of the ucontext.
I don't understand. Why are you recomputing MSR_IA32_U_CET here?
As another general complaint about this patch set, there's
cet->shstk_size and there's MSR_IA32_U_CET (and its copy in the fpu
state), and they seem to be used somewhat interchangably. Why are
both needed? Could there be some new helpers to help manage them all
in a unified way?
quoted hunk
+}
+
/*
* Signal frame handlers.
*/
@@ -295,6 +412,7 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size) struct task_struct *tsk = current; struct fpu *fpu = &tsk->thread.fpu; struct user_i387_ia32_struct env;+ struct sc_ext sc_ext; u64 user_xfeatures = 0; int fx_only = 0; int ret = 0;
@@ -335,6 +453,10 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size) if ((unsigned long)buf_fx % 64) fx_only = 1;+ ret = get_extra_state_from_sigframe(ia32_fxstate, buf, &sc_ext);+ if (ret)+ return ret;+ if (!ia32_fxstate) { /* * Attempt to restore the FPU registers directly from user
@@ -349,6 +471,8 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size) pagefault_enable(); if (!ret) {+ restore_extra_state(&sc_ext);+ /* * Restore supervisor states: previous context switch * etc has done XSAVES and saved the supervisor states
@@ -423,6 +547,8 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size) if (unlikely(init_bv)) copy_kernel_to_xregs(&init_fpstate.xsave, init_bv);+ restore_extra_state(&sc_ext);+ /* * Restore previously saved supervisor xstates along with * copied-in user xstates.
@@ -491,12 +617,29 @@ int fpu__restore_sig(void __user *buf, int ia32_frame) return __fpu__restore_sig(buf, buf_fx, size); }+static unsigned long fpu__alloc_sigcontext_ext(unsigned long sp)+{+#ifdef CONFIG_X86_CET+ struct cet_status *cet = ¤t->thread.cet;++ /*+ * sigcontext_ext is at: fpu + fpu_user_xstate_size ++ * FP_XSTATE_MAGIC2_SIZE, then aligned to 8.+ */+ if (cet->shstk_size)+ sp -= (sizeof(struct sc_ext) + 8);+#endif+ return sp;+}+ unsigned long fpu__alloc_mathframe(unsigned long sp, int ia32_frame, unsigned long *buf_fx, unsigned long *size) { unsigned long frame_size = xstate_sigframe_size();+ sp = fpu__alloc_sigcontext_ext(sp);+ *buf_fx = sp = round_down(sp - frame_size, 64); if (ia32_frame && use_fxsr()) { frame_size += sizeof(struct fregs_state);
On Thu, Apr 1, 2021 at 3:11 PM Yu-cheng Yu [off-list ref] wrote:
quoted
When shadow stack is enabled, a task's shadow stack states must be saved
along with the signal context and later restored in sigreturn. However,
currently there is no systematic facility for extending a signal context.
Introduce a signal context extension struct 'sc_ext', which is used to save
shadow stack restore token address and WAIT_ENDBR status[1]. The extension
is located above the fpu states, plus alignment.
Introduce routines for the allocation, save, and restore for sc_ext:
- fpu__alloc_sigcontext_ext(),
- save_extra_state_to_sigframe(),
- get_extra_state_from_sigframe(),
- restore_extra_state().
[1] WAIT_ENDBR will be introduced later in the Indirect Branch Tracking
series, but add that into sc_ext now to keep the struct stable in case
the IBT series is applied later.
Please don't. Instead, please figure out how that structure gets
extended for real, and organize your patches to demonstrate that the
extension works.
quoted
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
---
v24:
- Split out shadow stack token routines to a separate patch.
- Put signal frame save/restore routines to fpu/signal.c and re-name accordingly.
arch/x86/ia32/ia32_signal.c | 16 +++
arch/x86/include/asm/cet.h | 2 +
arch/x86/include/asm/fpu/internal.h | 2 +
arch/x86/include/uapi/asm/sigcontext.h | 9 ++
arch/x86/kernel/fpu/signal.c | 143 +++++++++++++++++++++++++
arch/x86/kernel/signal.c | 9 ++
6 files changed, 181 insertions(+)
@@ -196,6 +196,15 @@ struct _xstate {/* New processor state extensions go here: */};+/*+*Locatedattheendofsigcontext->fpstate,alignedto8.+*/+structsc_ext{+unsignedlongtotal_size;+unsignedlongssp;+unsignedlongwait_endbr;+};
We need some proper documentation and an extensibility story for this.
This won't be the last time we extend the signal state. Keep in mind
that the FPU state is very likely to become genuinely variable sized
due to AVX-512 and AMX.
Right now, on the signal stack, we have:
- siginfo, ucontext,
- fpu states (xsave state),
We might not want to change ucontext. The concern is breaking existing
app's.
Fpu states are all user states (vs. ssp, wait_endbr are supervisor
states). Therefore, we cannot put ssp and wait_endbr in fpu states.
Fpu states can grow to whatever size (AVX-512 etc.), the extension is
always above it if the user stack has room. If the user stack does not
have enough room, fpu__aloc_mathframe() fails.
The struct sc_ext has a simple 'total_size' field for error checking.
To extend it, newer fields are always added to the end and total_size
keeps track of it. I will put more comments about this.
We also have the ability to extend ucontext, I believe, and I'd like
some analysis of why we want to put ssp and wait_endbr into the FPU
context instead of the ucontext.
I don't understand. Why are you recomputing MSR_IA32_U_CET here?
As another general complaint about this patch set, there's
cet->shstk_size and there's MSR_IA32_U_CET (and its copy in the fpu
state), and they seem to be used somewhat interchangably. Why are
both needed? Could there be some new helpers to help manage them all
in a unified way?
Indeed, shadow stack/IBT states are cached in the thread header. Their
MSRs and XSAVES states are accessed only when necessary. The signal
restore path has been optimized in the past and I hope not to put in
code that negates past work.
I agree with your other comments for the patch and will update in the
next revision.
Thanks,
Yu-cheng
Right, I know we talked about having this synthetic flag but now that we
are moving to CONFIG_X86_SHADOW_STACK and separate SHSTK and IBT feature
bits, that synthetic flag is not needed anymore.
For the cases where you wanna test whether any of the two are present,
we're probably better off adding a x86_cet_enabled() helper which tests
SHSTK and IBT bits.
I haven't gone through the whole thing yet but depending on the context
and the fact that AMD doesn't support IBT, that helper might need some
tweaking too. I'll see.
quoted hunk
#define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */
#define X86_FEATURE_TSC_KNOWN_FREQ ( 3*32+31) /* TSC has known frequency */
And you don't need that config item either - AFAICT, you can use
CONFIG_X86_SHADOW_STACK everywhere.
Which would simplify that config space.
Thx.
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
From: Kirill A. Shutemov <hidden> Date: 2021-04-09 15:07:46
On Thu, Apr 01, 2021 at 03:10:46PM -0700, Yu-cheng Yu wrote:
When Shadow Stack is introduced, [R/O + _PAGE_DIRTY] PTE is reserved for
shadow stack. Copy-on-write PTEs have [R/O + _PAGE_COW].
When a PTE goes from [R/W + _PAGE_DIRTY] to [R/O + _PAGE_COW], it could
become a transient shadow stack PTE in two cases:
The first case is that some processors can start a write but end up seeing
a read-only PTE by the time they get to the Dirty bit, creating a transient
shadow stack PTE. However, this will not occur on processors supporting
Shadow Stack, and a TLB flush is not necessary.
The second case is that when _PAGE_DIRTY is replaced with _PAGE_COW non-
atomically, a transient shadow stack PTE can be created as a result.
Thus, prevent that with cmpxchg.
Dave Hansen, Jann Horn, Andy Lutomirski, and Peter Zijlstra provided many
insights to the issue. Jann Horn provided the cmpxchg solution.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
Reviewed-by: Kirill A. Shutemov <redacted>
--
Kirill A. Shutemov
From: Kirill A. Shutemov <hidden> Date: 2021-04-09 15:10:16
On Thu, Apr 01, 2021 at 03:10:47PM -0700, Yu-cheng Yu wrote:
A shadow stack PTE must be read-only and have _PAGE_DIRTY set. However,
read-only and Dirty PTEs also exist for copy-on-write (COW) pages. These
two cases are handled differently for page faults. Introduce
VM_SHADOW_STACK to track shadow stack VMAs.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
Reviewed-by: Kirill A. Shutemov <redacted>
--
Kirill A. Shutemov
From: Kirill A. Shutemov <hidden> Date: 2021-04-09 15:12:36
On Thu, Apr 01, 2021 at 03:10:48PM -0700, Yu-cheng Yu wrote:
Shadow stack accesses are those that are performed by the CPU where it
expects to encounter a shadow stack mapping. These accesses are performed
implicitly by CALL/RET at the site of the shadow stack pointer. These
accesses are made explicitly by shadow stack management instructions like
WRUSSQ.
Shadow stacks accesses to shadow-stack mapping can see 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.
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.
In handling a shadow stack page fault, verify it occurs within a shadow
stack mapping. It is always an error otherwise. For valid shadow stack
accesses, set FAULT_FLAG_WRITE to effect copy-on-write. Because clearing
_PAGE_DIRTY (vs. _PAGE_RW) is used to trigger the fault, shadow stack read
fault and shadow stack write fault are not differentiated and both are
handled as a write access.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
Reviewed-by: Kirill A. Shutemov <redacted>
--
Kirill A. Shutemov
From: Kirill A. Shutemov <hidden> Date: 2021-04-09 15:16:40
On Thu, Apr 01, 2021 at 03:10:49PM -0700, Yu-cheng Yu wrote:
quoted hunk
When serving a page fault, maybe_mkwrite() makes a PTE writable if its vma
has VM_WRITE.
A shadow stack vma has VM_SHADOW_STACK. Its PTEs have _PAGE_DIRTY, but not
_PAGE_WRITE. In fork(), _PAGE_DIRTY is cleared to cause copy-on-write,
and in the page fault handler, _PAGE_DIRTY is restored and the shadow stack
page is writable again.
Introduce an x86 version of maybe_mkwrite(), which sets proper PTE bits
according to VM flags.
Apply the same changes to maybe_pmd_mkwrite().
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
---
v24:
- Instead of doing arch_maybe_mkwrite(), overwrite maybe*_mkwrite() with x86
versions.
- Change VM_SHSTK to VM_SHADOW_STACK.
arch/x86/include/asm/pgtable.h | 8 ++++++++
arch/x86/mm/pgtable.c | 20 ++++++++++++++++++++
include/linux/mm.h | 2 ++
mm/huge_memory.c | 2 ++
4 files changed, 32 insertions(+)
From: Kirill A. Shutemov <hidden> Date: 2021-04-09 15:20:17
On Thu, Apr 01, 2021 at 03:10:50PM -0700, Yu-cheng Yu wrote:
When serving a page fault, maybe_mkwrite() makes a PTE writable if it is in
a writable vma. A shadow stack vma is writable, but its PTEs need
_PAGE_DIRTY to be set to become writable. For this reason, maybe_mkwrite()
has been updated.
There are a few places that call pte_mkwrite() directly, but have the
same result as from maybe_mkwrite(). These sites need to be updated for
shadow stack as well. Thus, change them to maybe_mkwrite():
- do_anonymous_page() and migrate_vma_insert_page() check VM_WRITE directly
and call pte_mkwrite(), which is the same as maybe_mkwrite(). Change
them to maybe_mkwrite().
- In do_numa_page(), if the numa entry was writable, then pte_mkwrite()
is called directly. Fix it by doing maybe_mkwrite().
- In change_pte_range(), pte_mkwrite() is called directly. Replace it with
maybe_mkwrite().
A shadow stack vma is writable but has different vma
flags, and handled accordingly in maybe_mkwrite().
Have you checked THP side? Looks like at least do_huge_pmd_numa_page()
needs adjustment, no?
--
Kirill A. Shutemov
From: Kirill A. Shutemov <hidden> Date: 2021-04-09 15:23:00
On Thu, Apr 01, 2021 at 03:10:51PM -0700, Yu-cheng Yu wrote:
quoted hunk
INCSSP(Q/D) increments shadow stack pointer and 'pops and discards' the
first and the last elements in the range, effectively touches those memory
areas.
The maximum moving distance by INCSSPQ is 255 * 8 = 2040 bytes and
255 * 4 = 1020 bytes by INCSSPD. Both ranges are far from PAGE_SIZE.
Thus, putting a gap page on both ends of a shadow stack prevents INCSSP,
CALL, and RET from going beyond.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
---
v24:
- Instead changing vm_*_gap(), create x86-specific versions.
arch/x86/include/asm/page_types.h | 17 +++++++++++++++
arch/x86/mm/mmap.c | 36 +++++++++++++++++++++++++++++++
include/linux/mm.h | 4 ++++
3 files changed, 57 insertions(+)
@@ -897,3 +897,10 @@ int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)#endif /* CONFIG_X86_64 */#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */++#ifdef CONFIG_ARCH_HAS_SHADOW_STACK+boolis_shadow_stack_mapping(vm_flags_tvm_flags)+{+return(vm_flags&VM_SHADOW_STACK);+}
No, just define it as you have here in linux/mm.h. It will always be false
for !CONFIG_ARCH_HAS_SHADOW_STACK as VM_SHADOW_STACK is 0 there.
This maze of #ifdefs are unneeded.
--
Kirill A. Shutemov
From: Kirill A. Shutemov <hidden> Date: 2021-04-09 15:31:55
On Thu, Apr 01, 2021 at 03:10:53PM -0700, Yu-cheng Yu wrote:
quoted hunk
Can_follow_write_pte() ensures a read-only page is COWed by checking the
FOLL_COW flag, and uses pte_dirty() to validate the flag is still valid.
Like a writable data page, a shadow stack page is writable, and becomes
read-only during copy-on-write, but it is always dirty. Thus, in the
can_follow_write_pte() check, it belongs to the writable page case and
should be excluded from the read-only page pte_dirty() check. Apply
the same changes to can_follow_write_pmd().
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
Cc: Kirill A. Shutemov <redacted>
---
v24:
- Change arch_shadow_stack_mapping() to is_shadow_stack_mapping().
mm/gup.c | 8 +++++---
mm/huge_memory.c | 8 +++++---
2 files changed, 10 insertions(+), 6 deletions(-)
@@ -356,10 +356,12 @@ static int follow_pfn_pte(struct vm_area_struct *vma, unsigned long address,*FOLL_FORCEcanwritetoevenunwritablepte's,butonly*afterwe'vegonethroughaCOWcycleandtheyaredirty.*/-staticinlineboolcan_follow_write_pte(pte_tpte,unsignedintflags)+staticinlineboolcan_follow_write_pte(pte_tpte,unsignedintflags,+structvm_area_struct*vma){returnpte_write(pte)||-((flags&FOLL_FORCE)&&(flags&FOLL_COW)&&pte_dirty(pte));+((flags&FOLL_FORCE)&&(flags&FOLL_COW)&&pte_dirty(pte)&&+!is_shadow_stack_mapping(vma->vm_flags));
It's getting too ugly. I think it deserve to be rewritten. What about:
if (pte_write(pte))
return true;
if ((flags & (FOLL_FORCE | FOLL_COW)) != (FOLL_FORCE | FOLL_COW))
return false;
if (!pte_dirty(pte))
return false;
if (is_shadow_stack_mapping(vma->vm_flags))
return false;
return true;
?
--
Kirill A. Shutemov
From: Kirill A. Shutemov <hidden> Date: 2021-04-09 15:34:29
On Thu, Apr 01, 2021 at 03:10:54PM -0700, Yu-cheng Yu wrote:
quoted hunk
In change_pte_range(), when a PTE is changed for prot_numa, _PAGE_RW is
preserved to avoid the additional write fault after the NUMA hinting fault.
However, pte_write() now includes both normal writable and shadow stack
(RW=0, Dirty=1) PTEs, but the latter does not have _PAGE_RW and has no need
to preserve it.
Exclude shadow stack from preserve_write test, and apply the same change to
change_huge_pmd().
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kirill A. Shutemov <redacted>
---
v24:
- Change arch_shadow_stack_mapping() to is_shadow_stack_mapping().
mm/huge_memory.c | 7 ++++++-
mm/mprotect.c | 9 ++++++++-
2 files changed, 14 insertions(+), 2 deletions(-)
Right, I know we talked about having this synthetic flag but now that we
are moving to CONFIG_X86_SHADOW_STACK and separate SHSTK and IBT feature
bits, that synthetic flag is not needed anymore.
For the cases where you wanna test whether any of the two are present,
we're probably better off adding a x86_cet_enabled() helper which tests
SHSTK and IBT bits.
Recall we had complicated code for the XSAVES features detection in
xstate.c. Dave Hansen proposed the solution and then the whole thing
becomes simple. Because of this flag, even when only the shadow stack
is available, the code handles it nicely.
I haven't gone through the whole thing yet but depending on the context
and the fact that AMD doesn't support IBT, that helper might need some
tweaking too. I'll see.
quoted
#define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */
#define X86_FEATURE_TSC_KNOWN_FREQ ( 3*32+31) /* TSC has known frequency */
And you don't need that config item either - AFAICT, you can use
CONFIG_X86_SHADOW_STACK everywhere.
Which would simplify that config space.
Would this equal to only CONFIG_X86_CET (one Kconfig option)? In fact,
when you proposed only CONFIG_X86_CET, things became much simpler.
Practically, IBT is not much in terms of code size. Since we have
already separated the two, why don't we leave it as-is. When people
start using it more, there will be more feedback, and we can decide if
one Kconfig is better?
Thanks,
Yu-cheng
@@ -535,6 +536,10 @@ struct thread_struct {unsignedintsig_on_uaccess_err:1;+#ifdef CONFIG_X86_CET+structcet_statuscet;+#endif+/* Floating point and extended processor state */structfpufpu;/*
If all callers pass down flags==0, populate will never happen.
+
+ return addr;
+}
+
+int shstk_setup(void)
+{
+ unsigned long addr, size;
+ struct cet_status *cet = ¤t->thread.cet;
+
+ if (!cpu_feature_enabled(X86_FEATURE_SHSTK))
+ return -EOPNOTSUPP;
+
+ size = round_up(min_t(unsigned long long, rlimit(RLIMIT_STACK), SZ_4G), PAGE_SIZE);
+ addr = alloc_shstk(size, 0);
+ if (IS_ERR_VALUE(addr))
+ return PTR_ERR((void *)addr);
+
+ cet->shstk_base = addr;
+ cet->shstk_size = size;
+
+ start_update_msrs();
+ wrmsrl(MSR_IA32_PL3_SSP, addr + size);
+ wrmsrl(MSR_IA32_U_CET, CET_SHSTK_EN);
+ end_update_msrs();
+ return 0;
+}
+
+void shstk_free(struct task_struct *tsk)
+{
+ struct cet_status *cet = &tsk->thread.cet;
+
+ if (!cpu_feature_enabled(X86_FEATURE_SHSTK) ||
+ !cet->shstk_size ||
+ !cet->shstk_base)
+ return;
+
+ if (!tsk->mm)
+ return;
+
+ while (1) {
+ int r;
+
+ r = vm_munmap(cet->shstk_base, cet->shstk_size);
+
+ /*
+ * vm_munmap() returns -EINTR when mmap_lock is held by
+ * something else, and that lock should not be held for a
+ * long time. Retry it for the case.
+ */
Hm, no. -EINTR is not about the lock being held by somebody else. The task
got a signal and need to return to userspace.
I have not looked at the rest of the patches yet, but why do you need a
special free path for shadow stack? Why the normal unmap route doesn't
work for you?
On Fri, Apr 09, 2021 at 08:52:52AM -0700, Yu, Yu-cheng wrote:
Recall we had complicated code for the XSAVES features detection in
xstate.c. Dave Hansen proposed the solution and then the whole thing
becomes simple. Because of this flag, even when only the shadow stack is
available, the code handles it nicely.
Would this equal to only CONFIG_X86_CET (one Kconfig option)? In fact, when
you proposed only CONFIG_X86_CET, things became much simpler.
When you use CONFIG_X86_SHADOW_STACK instead, it should remain same
simple no?
Practically, IBT is not much in terms of code size. Since we have already
separated the two, why don't we leave it as-is. When people start using it
more, there will be more feedback, and we can decide if one Kconfig is
better?
Because when we add stuff to the kernel, we add the simplest and
cleanest version possible and later, when we determine that additional
functionality is needed, *then* we add it. Not the other way around.
Our Kconfig symbol space is already an abomination so we can't just add
some more and decide later.
What happens in such situations usually is stuff gets added, it bitrots
and some poor soul - very likely a maintainer who has to mop up after
everybody - comes and cleans it up. I'd like to save myself that
cleaning up.
Thx.
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
On Fri, Apr 09, 2021 at 08:52:52AM -0700, Yu, Yu-cheng wrote:
quoted
Recall we had complicated code for the XSAVES features detection in
xstate.c. Dave Hansen proposed the solution and then the whole thing
becomes simple. Because of this flag, even when only the shadow stack is
available, the code handles it nicely.
Would this equal to only CONFIG_X86_CET (one Kconfig option)? In fact, when
you proposed only CONFIG_X86_CET, things became much simpler.
When you use CONFIG_X86_SHADOW_STACK instead, it should remain same
simple no?
Signals, arch_prctl, and ELF header are three places that need to depend
on either shadow stack or IBT is configured. To remain simple, we can
make all three depend on CONFIG_X86_SHADOW_STACK, and in Kconfig, make
CONFIG_X86_IBT depend on CONFIG_X86_SHADOW_STACK. Without shadow stack,
IBT itself is not as useful anyway.
quoted
Practically, IBT is not much in terms of code size. Since we have already
separated the two, why don't we leave it as-is. When people start using it
more, there will be more feedback, and we can decide if one Kconfig is
better?
Because when we add stuff to the kernel, we add the simplest and
cleanest version possible and later, when we determine that additional
functionality is needed, *then* we add it. Not the other way around.
Our Kconfig symbol space is already an abomination so we can't just add
some more and decide later.
What happens in such situations usually is stuff gets added, it bitrots
and some poor soul - very likely a maintainer who has to mop up after
everybody - comes and cleans it up. I'd like to save myself that
cleaning up.
Thx.
On Thu, Apr 01, 2021 at 03:10:56PM -0700, Yu-cheng Yu wrote:
quoted
Introduce basic shadow stack enabling/disabling/allocation routines.
A task's shadow stack is allocated from memory with VM_SHADOW_STACK flag
and has a fixed size of min(RLIMIT_STACK, 4GB).
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Kees Cook <redacted>
If all callers pass down flags==0, populate will never happen.
I will fix it.
quoted
+
+ return addr;
+}
+
+int shstk_setup(void)
+{
+ unsigned long addr, size;
+ struct cet_status *cet = ¤t->thread.cet;
+
+ if (!cpu_feature_enabled(X86_FEATURE_SHSTK))
+ return -EOPNOTSUPP;
+
+ size = round_up(min_t(unsigned long long, rlimit(RLIMIT_STACK), SZ_4G), PAGE_SIZE);
+ addr = alloc_shstk(size, 0);
+ if (IS_ERR_VALUE(addr))
+ return PTR_ERR((void *)addr);
+
+ cet->shstk_base = addr;
+ cet->shstk_size = size;
+
+ start_update_msrs();
+ wrmsrl(MSR_IA32_PL3_SSP, addr + size);
+ wrmsrl(MSR_IA32_U_CET, CET_SHSTK_EN);
+ end_update_msrs();
+ return 0;
+}
+
+void shstk_free(struct task_struct *tsk)
+{
+ struct cet_status *cet = &tsk->thread.cet;
+
+ if (!cpu_feature_enabled(X86_FEATURE_SHSTK) ||
+ !cet->shstk_size ||
+ !cet->shstk_base)
+ return;
+
+ if (!tsk->mm)
+ return;
+
+ while (1) {
+ int r;
+
+ r = vm_munmap(cet->shstk_base, cet->shstk_size);
+
+ /*
+ * vm_munmap() returns -EINTR when mmap_lock is held by
+ * something else, and that lock should not be held for a
+ * long time. Retry it for the case.
+ */
Hm, no. -EINTR is not about the lock being held by somebody else. The task
got a signal and need to return to userspace.
From tracing the code itself, it looks like it cannot acquire the lock.
Let me dig into it.
I have not looked at the rest of the patches yet, but why do you need a
special free path for shadow stack? Why the normal unmap route doesn't
work for you?
The thread's shadow stack is allocated by the kernel, so it needs to be
freed when the thread exits.
Signals, arch_prctl, and ELF header are three places that need to depend on
either shadow stack or IBT is configured. To remain simple, we can make all
three depend on CONFIG_X86_SHADOW_STACK, and in Kconfig, make CONFIG_X86_IBT
depend on CONFIG_X86_SHADOW_STACK. Without shadow stack, IBT itself is not
as useful anyway.