There was no more caller passing vm_flags to do_mmap(), and vm_flags was
removed from the function's input by:
commit 45e55300f114 ("mm: remove unnecessary wrapper function do_mmap_pgoff()").
There is a new user now. Shadow stack allocation passes VM_SHSTK to
do_mmap(). Re-introduce vm_flags to do_mmap(), but without the old wrapper
do_mmap_pgoff(). Instead, make all callers of the wrapper pass a zero
vm_flags to do_mmap().
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Peter Collingbourne <redacted>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: linux-mm@kvack.org
v14:
- Remove do_mmap_pgoff() and make all callers of do_mmap() pass vm_flags.
---
fs/aio.c | 2 +-
include/linux/mm.h | 3 ++-
ipc/shm.c | 2 +-
mm/mmap.c | 10 +++++-----
mm/nommu.c | 4 ++--
mm/util.c | 2 +-
6 files changed, 12 insertions(+), 11 deletions(-)
@@ -1078,6 +1078,7 @@ unsigned long do_mmap(struct file *file,unsignedlonglen,unsignedlongprot,unsignedlongflags,+vm_flags_tvm_flags,unsignedlongpgoff,unsignedlong*populate,structlist_head*uf)
@@ -1085,7 +1086,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;
@@ -1104,7 +1104,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);
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>
v10:
- Define ARCH_SHADOW_STACK_GUARD_GAP.
---
arch/x86/include/asm/processor.h | 10 ++++++++++
include/linux/mm.h | 24 ++++++++++++++++++++----
2 files changed, 30 insertions(+), 4 deletions(-)
A shadow stack page is made writable by pte_mkwrite_shstk(), which sets
_PAGE_DIRTY_HW. There are a few places that call pte_mkwrite() directly
and miss the maybe_mkwrite() fixup in the previous patch. Fix them with
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().
Signed-off-by: Yu-cheng Yu <redacted>
---
mm/memory.c | 5 ++---
mm/migrate.c | 3 +--
mm/mprotect.c | 2 +-
3 files changed, 4 insertions(+), 6 deletions(-)
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_HW (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>
v10:
-Revise commit log.
---
arch/x86/include/asm/traps.h | 2 ++
arch/x86/mm/fault.c | 19 +++++++++++++++++++
2 files changed, 21 insertions(+)
When shadow stack is introduced, [R/O + _PAGE_DIRTY_HW] PTE is reserved
for shadow stack. Copy-on-write PTEs have [R/O + _PAGE_COW].
When a PTE goes from [R/W + _PAGE_DIRTY_HW] 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, therefore we don't need a TLB flush here.
The second case is that when the software, without atomic, tests & replaces
_PAGE_DIRTY_HW with _PAGE_COW, a transient shadow stack PTE can exist.
This is prevented 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>
v10:
- Replace bit shift with pte_wrprotect()/pmd_wrprotect(), which use bit
test & shift.
- Move READ_ONCE of old_pte into try_cmpxchg() loop.
- Change static_cpu_has() to cpu_feature_enabled().
v9:
- Change compile-time conditionals to runtime checks.
- Fix parameters of try_cmpxchg(): change pte_t/pmd_t to
pte_t.pte/pmd_t.pmd.
v4:
- Implement try_cmpxchg().
---
arch/x86/include/asm/pgtable.h | 52 ++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
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_SHSTK to
track shadow stack VMAs.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
v9:
- Add VM_SHSTK case to arch_vma_name().
- Revise the commit log to explain why adding a new VM flag.
---
arch/x86/mm/mmap.c | 2 ++
fs/proc/task_mmu.c | 3 +++
include/linux/mm.h | 8 ++++++++
3 files changed, 13 insertions(+)
Pte_modify() changes a PTE to 'newprot'. It doesn't use the pte_*()
helpers that a previous patch fixed up, so we need a new site.
Introduce fixup_dirty_pte() to set the dirty bits based on _PAGE_RW, and
apply the same changes to pmd_modify().
Signed-off-by: Yu-cheng Yu <redacted>
v14:
- Update fixup_dirty_pte() and fixup_dirty_pmd() with
cpu_feature_enabled(X86_FEATURE_SHSTK).
v10:
- Replace _PAGE_CHG_MASK approach with fixup functions.
---
arch/x86/include/asm/pgtable.h | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
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>
v10:
- Reverse name changes to can_follow_write_*().
---
mm/gup.c | 8 +++++---
mm/huge_memory.c | 8 +++++---
2 files changed, 10 insertions(+), 6 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_HW),0);}intset_memory_rw(unsignedlongaddr,intnumpages)
After the introduction of _PAGE_COW, a modified page's PTE can have either
_PAGE_DIRTY_HW or _PAGE_COW. Change _PAGE_DIRTY to _PAGE_DIRTY_BITS.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <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(-)
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.
But that leaves us with a Linux problem: we need to ensure we never create
Write=0,Dirty=1 PTEs. In places where we do create them, we need to find
an alternative way to represent them _without_ using the same hardware bit
combination. Thus, enter _PAGE_COW. This results in the following:
(a) A modified, copy-on-write (COW) page: (R/O + _PAGE_COW)
(b) A R/O page that has been COW'ed: (R/O + _PAGE_COW)
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 R/O and _PAGE_COW.
(c) A shadow stack PTE: (R/O + _PAGE_DIRTY_HW)
(d) A shared shadow stack PTE: (R/O + _PAGE_COW)
When a shadow stack page is being shared among processes (this happens
at fork()), its PTE is cleared of _PAGE_DIRTY_HW, so the next shadow
stack access causes a fault, and the page is duplicated and
_PAGE_DIRTY_HW 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.
Use _PAGE_COW in pte_wrprotect() and _PAGE_DIRTY_HW in pte_mkwrite().
Apply the same changes to pmd and pud.
When this patch is applied, there are six free bits left in the 64-bit PTE.
There are 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: Kees Cook <redacted>
v10:
- Change _PAGE_BIT_DIRTY_SW to _PAGE_BIT_COW, as it is used for copy-on-
write PTEs.
- Update pte_write() and treat shadow stack as writable.
- Change *_mkdirty_shstk() to *_mkwrite_shstk() as these make shadow stack
pages writable.
- Use bit test & shift to move _PAGE_BIT_DIRTY_HW to _PAGE_BIT_COW.
- Change static_cpu_has() to cpu_feature_enabled().
- Revise commit log.
v9:
- Remove pte_move_flags() etc. and put the logic directly in
pte_wrprotect()/pte_mkwrite() etc.
- Change compile-time conditionals to run-time checks.
- Split out pte_modify()/pmd_modify() to a new patch.
- Update comments.
---
arch/x86/include/asm/pgtable.h | 120 ++++++++++++++++++++++++---
arch/x86/include/asm/pgtable_types.h | 41 ++++++++-
2 files changed, 150 insertions(+), 11 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,16 @@#define _PAGE_BIT_SOFT_DIRTY _PAGE_BIT_SOFTW3 /* software dirty tracking */#define _PAGE_BIT_DEVMAP _PAGE_BIT_SOFTW4+/*+*Thisbitindicatesacopy-on-writepage,andisdifferentfrom+*_PAGE_BIT_SOFT_DIRTY,whichtrackswhichpagesataskwritesto.+*/+#ifdef CONFIG_X86_64+#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
Shadow Stack provides protection against function return address
corruption. It is active when the processor supports it, the kernel has
CONFIG_X86_SHADOW_STACK_USER, 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>
v13:
- Update help text, and change default to N.
- Change X86_INTEL_* to X86_*.
v10:
- Change SHSTK to shadow stack in the help text.
- Change build-time check to config-time check.
- Change ARCH_HAS_SHSTK to ARCH_HAS_SHADOW_STACK.
---
arch/x86/Kconfig | 33 +++++++++++++++++++++++++++
scripts/as-x86_64-has-shadow-stack.sh | 4 ++++
2 files changed, 37 insertions(+)
create mode 100755 scripts/as-x86_64-has-shadow-stack.sh
Before introducing _PAGE_COW for non-hardware memory management purposes in
the next patch, rename _PAGE_DIRTY to _PAGE_DIRTY_HW and _PAGE_BIT_DIRTY to
_PAGE_BIT_DIRTY_HW to make meanings more clear. There are no functional
changes from this patch.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
Reviewed-by: Dave Hansen <redacted>
v9:
- At some places _PAGE_DIRTY were not changed to _PAGE_DIRTY_HW, because
they will be changed again in the next patch to _PAGE_DIRTY_BITS.
However, this causes compile issues if the next patch is not yet applied.
Fix it by changing all _PAGE_DIRTY to _PAGE_DRITY_HW.
---
arch/x86/include/asm/pgtable.h | 18 +++++++++---------
arch/x86/include/asm/pgtable_types.h | 11 +++++------
arch/x86/kernel/relocate_kernel_64.S | 2 +-
arch/x86/kvm/vmx/vmx.c | 2 +-
4 files changed, 16 insertions(+), 17 deletions(-)
@@ -3620,7 +3620,7 @@ static int init_rmode_identity_map(struct kvm *kvm)/* Set up identity-mapping pagetable for EPT in real mode */for(i=0;i<PT32_ENT_PER_PAGE;i++){tmp=(i<<22)+(_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|-_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_PSE);+_PAGE_ACCESSED|_PAGE_DIRTY_HW|_PAGE_PSE);r=kvm_write_guest_page(kvm,identity_map_pfn,&tmp,i*sizeof(tmp),sizeof(tmp));if(r<0)
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>
Reviewed-by: Kees Cook <redacted>
v13:
- Change X86_INTEL_* to X86_*.
v10:
- Change CONFIG_X86_64 to CONFIG_X86_INTEL_CET.
v9:
- Add Shadow Stack pointer to the fault printout.
---
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 | 59 ++++++++++++++++++++++++++++++
include/uapi/asm-generic/siginfo.h | 3 +-
5 files changed, 70 insertions(+), 2 deletions(-)
@@ -532,6 +532,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);
@@ -597,6 +597,65 @@ 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",+};++/*+*Whenacontrolprotectionexceptionoccurs,sendasignal+*totheresponsibleapplication.Currently,control+*protectionisonlyenabledfortheusermode.This+*exceptionshouldnotcomefromthekernelmode.+*/+DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)+{+structtask_struct*tsk;++if(notify_die(DIE_TRAP,"control protection fault",regs,+error_code,X86_TRAP_CP,SIGSEGV)==NOTIFY_STOP)+return;+cond_local_irq_enable(regs);++if(!user_mode(regs))+die("kernel control protection fault",regs,error_code);++if(!static_cpu_has(X86_FEATURE_SHSTK)&&+!static_cpu_has(X86_FEATURE_IBT))+WARN_ONCE(1,"CET is disabled but got control protection fault\n");++tsk=current;+tsk->thread.error_code=error_code;+tsk->thread.trap_nr=X86_TRAP_CP;++if(show_unhandled_signals&&unhandled_signal(tsk,SIGSEGV)&&+printk_ratelimit()){+unsignedintmax_err;+unsignedlongssp;++max_err=ARRAY_SIZE(control_protection_err)-1;+if((error_code<0)||(error_code>max_err))+error_code=0;+rdmsrl(MSR_IA32_PL3_SSP,ssp);+pr_info("%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[error_code]);+print_vma_addr(KERN_CONT" in ",regs->ip);+pr_cont("\n");+}++force_sig_fault(SIGSEGV,SEGV_CPERR,+(void__user*)uprobe_get_trap_addr(regs));+cond_local_irq_disable(regs);+}+#endif+staticbooldo_int3(structpt_regs*regs){intres;
@@ -35,7 +35,7 @@XFEATURE_MASK_BNDCSR)/* All currently supported supervisor features */-#define XFEATURE_MASK_SUPERVISOR_SUPPORTED (0)+#define XFEATURE_MASK_SUPERVISOR_SUPPORTED (XFEATURE_MASK_CET_USER)/**Asupervisorstatecomponentmaynotalwayscontainvaluableinformation,
@@ -62,7 +62,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 | \
Explain no_user_shstk/no_user_ibt kernel parameters, and introduce a new
document on Control-flow Enforcement Technology (CET).
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
v13:
- Change X86_INTEL_* to X86_*.
v12:
- Remove ARCH_X86_CET_MMAP_SHSTK information.
v11:
- Add back GLIBC tunables information.
- Add ARCH_X86_CET_MMAP_SHSTK information.
v10:
- Change no_cet_shstk and no_cet_ibt to no_user_shstk and no_user_ibt.
- Remove the opcode section, as it is already in the Intel SDM.
- Remove sections related to GLIBC implementation.
- Remove shadow stack memory management section, as it is already in the
code comments.
- Remove legacy bitmap related information, as it is not supported now.
- Fix arch_ioctl() related text.
- Change SHSTK, IBT to plain English.
---
.../admin-guide/kernel-parameters.txt | 6 +
Documentation/x86/index.rst | 1 +
Documentation/x86/intel_cet.rst | 133 ++++++++++++++++++
3 files changed, 140 insertions(+)
create mode 100644 Documentation/x86/intel_cet.rst
@@ -0,0 +1,133 @@+.. 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, 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 kernel configuration options:++ X86_SHADOW_STACK_USER, and+ X86_BRANCH_TRACKING_USER.++These need to be enabled to build a CET-enabled kernel, and Binutils v2.31+and GCC v8.1 or later are required to build a CET kernel. 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 the following command output, in the NT_GNU_PROPERTY_TYPE_0+field:++ readelf -n <application>++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 tunables for backward compatibility.++GLIBC_TUNABLES=glibc.tune.hwcaps=-SHSTK,-IBT+ Turn off SHSTK/IBT for the current shell.++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.++[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_SHSTK 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.
Control-flow Enforcement (CET) is a new Intel processor feature that blocks
return/jump-oriented programming attacks. Details are in "Intel 64 and
IA-32 Architectures Software Developer's Manual" [1].
[...]
Hi,
The series did not get send out completely. Please ignore it.
I apologize for the noise, and will send it again later.
Yu-cheng