Skip to content
6 changes: 3 additions & 3 deletions build/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,7 @@ alias asm_sources
asm/jump_x86_64_sysv_elf_gas.S
asm/ontop_x86_64_sysv_elf_gas.S
: <abi>x32
<address-model>64
<address-model>32
<architecture>x86
<binary-format>elf
<toolset>clang
Expand All @@ -808,7 +808,7 @@ alias asm_sources
asm/jump_x86_64_sysv_elf_gas.S
asm/ontop_x86_64_sysv_elf_gas.S
: <abi>x32
<address-model>64
<address-model>32
<architecture>x86
<binary-format>elf
<toolset>gcc
Expand All @@ -819,7 +819,7 @@ alias asm_sources
asm/jump_x86_64_sysv_elf_gas.S
asm/ontop_x86_64_sysv_elf_gas.S
: <abi>x32
<address-model>64
<address-model>32
<architecture>x86
<binary-format>elf
<toolset>intel
Expand Down
16 changes: 16 additions & 0 deletions doc/stack.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -364,4 +364,20 @@ when linking against Boost binaries.
[endsect]


[section:stack_protect Support for stack protection]

Compiler switch `-fstack-protector` changes the default context switching logic.
Users must define `BOOST_CONTEXT_TLS_STACK_PROTECTOR` before including any
Boost.Context headers if stack protection is enabled.

[endsect]

[section:shadow_stack Support for shadow stack protection]

Shadow stack is part of Intel's Control-Flow Enforcement Technology. Users must
define `BOOST_CONTEXT_SHADOW_STACK` before including any Boost.Context headers
if shadow stack protection is enabled.

[endsect]

[endsect]
55 changes: 55 additions & 0 deletions include/boost/context/continuation_fcontext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@
# include BOOST_ABI_PREFIX
#endif

#if defined __CET__
# include <cet.h>
# include <sys/mman.h>
# define SHSTK_ENABLED (__CET__ & 0x2)
# define BOOST_CONTEXT_SHADOW_STACK (SHSTK_ENABLED && SHADOW_STACK_SYSCALL)
# define __NR_map_shadow_stack 451
#ifndef SHADOW_STACK_SET_TOKEN
# define SHADOW_STACK_SET_TOKEN 0x1
#endif
#endif

#if defined(BOOST_MSVC)
# pragma warning(push)
# pragma warning(disable: 4702)
Expand All @@ -62,6 +73,12 @@ transfer_t context_unwind( transfer_t t) {
template< typename Rec >
transfer_t context_exit( transfer_t t) noexcept {
Rec * rec = static_cast< Rec * >( t.data);
#if BOOST_CONTEXT_SHADOW_STACK
// destory shadow stack
std::size_t ss_size = *((unsigned long*)(reinterpret_cast< uintptr_t >( rec)- 16));
long unsigned int ss_base = *((unsigned long*)(reinterpret_cast< uintptr_t >( rec)- 8));
munmap((void *)ss_base, ss_size);
#endif
// destroy context stack
rec->deallocate();
return { nullptr, nullptr };
Expand Down Expand Up @@ -168,6 +185,25 @@ fcontext_t create_context1( StackAlloc && salloc, Fn && fn) {
reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
// create fast-context
const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);

#if BOOST_CONTEXT_SHADOW_STACK
std::size_t ss_size = size >> 5;
// align shadow stack to 8 bytes.
ss_size = (ss_size + 7) & ~7;
// Todo: shadow stack occupies at least 4KB
ss_size = (ss_size > 4096) ? size : 4096;
// create shadow stack
void *ss_base = (void *)syscall(__NR_map_shadow_stack, 0, ss_size, SHADOW_STACK_SET_TOKEN);
BOOST_ASSERT(ss_base != -1);
unsigned long ss_sp = (unsigned long)ss_base + ss_size;
/* pass the shadow stack pointer to make_fcontext
i.e., link the new shadow stack with the new fcontext
TODO should be a better way? */
*((unsigned long*)(reinterpret_cast< uintptr_t >( stack_top)- 8)) = ss_sp;
/* Todo: place shadow stack info in 64byte gap */
*((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 8)) = (unsigned long) ss_base;
*((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 16)) = ss_size;
#endif
const fcontext_t fctx = make_fcontext( stack_top, size, & context_entry< Record >);
BOOST_ASSERT( nullptr != fctx);
// transfer control structure to context-stack
Expand All @@ -190,6 +226,25 @@ fcontext_t create_context2( preallocated palloc, StackAlloc && salloc, Fn && fn)
reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) );
// create fast-context
const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);

#if BOOST_CONTEXT_SHADOW_STACK
std::size_t ss_size = size >> 5;
// align shadow stack to 8 bytes.
ss_size = (ss_size + 7) & ~7;
// Todo: shadow stack occupies at least 4KB
ss_size = (ss_size > 4096) ? size : 4096;
// create shadow stack
void *ss_base = (void *)syscall(__NR_map_shadow_stack, 0, ss_size, SHADOW_STACK_SET_TOKEN);
BOOST_ASSERT(ss_base != -1);
unsigned long ss_sp = (unsigned long)ss_base + ss_size;
/* pass the shadow stack pointer to make_fcontext
i.e., link the new shadow stack with the new fcontext
TODO should be a better way? */
*((unsigned long*)(reinterpret_cast< uintptr_t >( stack_top)- 8)) = ss_sp;
/* Todo: place shadow stack info in 64byte gap */
*((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 8)) = (unsigned long) ss_base;
*((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 16)) = ss_size;
#endif
const fcontext_t fctx = make_fcontext( stack_top, size, & context_entry< Record >);
BOOST_ASSERT( nullptr != fctx);
// transfer control structure to context-stack
Expand Down
63 changes: 55 additions & 8 deletions include/boost/context/fiber_fcontext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@
# include BOOST_ABI_PREFIX
#endif

#if defined __CET__
# include <cet.h>
# include <sys/mman.h>
# define SHSTK_ENABLED (__CET__ & 0x2)
# define BOOST_CONTEXT_SHADOW_STACK (SHSTK_ENABLED && SHADOW_STACK_SYSCALL)
# define __NR_map_shadow_stack 451
#ifndef SHADOW_STACK_SET_TOKEN
# define SHADOW_STACK_SET_TOKEN 0x1
#endif
#endif

#if defined(BOOST_MSVC)
# pragma warning(push)
# pragma warning(disable: 4702)
Expand All @@ -62,6 +73,12 @@ transfer_t fiber_unwind( transfer_t t) {
template< typename Rec >
transfer_t fiber_exit( transfer_t t) noexcept {
Rec * rec = static_cast< Rec * >( t.data);
#if BOOST_CONTEXT_SHADOW_STACK
// destory shadow stack
std::size_t ss_size = *((unsigned long*)(reinterpret_cast< uintptr_t >( rec)- 16));
long unsigned int ss_base = *((unsigned long*)(reinterpret_cast< uintptr_t >( rec)- 8));
munmap((void *)ss_base, ss_size);
#endif
// destroy context stack
rec->deallocate();
return { nullptr, nullptr };
Expand Down Expand Up @@ -165,6 +182,25 @@ fcontext_t create_fiber1( StackAlloc && salloc, Fn && fn) {
reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
// create fast-context
const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);

#if BOOST_CONTEXT_SHADOW_STACK
std::size_t ss_size = size >> 5;
// align shadow stack to 8 bytes.
ss_size = (ss_size + 7) & ~7;
// Todo: shadow stack occupies at least 4KB
ss_size = (ss_size > 4096) ? size : 4096;
// create shadow stack
void *ss_base = (void *)syscall(__NR_map_shadow_stack, 0, ss_size, SHADOW_STACK_SET_TOKEN);
BOOST_ASSERT(ss_base != -1);
unsigned long ss_sp = (unsigned long)ss_base + ss_size;
/* pass the shadow stack pointer to make_fcontext
i.e., link the new shadow stack with the new fcontext
TODO should be a better way? */
*((unsigned long*)(reinterpret_cast< uintptr_t >( stack_top)- 8)) = ss_sp;
/* Todo: place shadow stack info in 64byte gap */
*((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 8)) = (unsigned long) ss_base;
*((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 16)) = ss_size;
#endif
const fcontext_t fctx = make_fcontext( stack_top, size, & fiber_entry< Record >);
BOOST_ASSERT( nullptr != fctx);
// transfer control structure to context-stack
Expand All @@ -187,6 +223,25 @@ fcontext_t create_fiber2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) );
// create fast-context
const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);

#if BOOST_CONTEXT_SHADOW_STACK
std::size_t ss_size = size >> 5;
// align shadow stack to 8 bytes.
ss_size = (ss_size + 7) & ~7;
// Todo: shadow stack occupies at least 4KB
ss_size = (ss_size > 4096) ? size : 4096;
// create shadow stack
void *ss_base = (void *)syscall(__NR_map_shadow_stack, 0, ss_size, SHADOW_STACK_SET_TOKEN);
BOOST_ASSERT(ss_base != -1);
unsigned long ss_sp = (unsigned long)ss_base + ss_size;
/* pass the shadow stack pointer to make_fcontext
i.e., link the new shadow stack with the new fcontext
TODO should be a better way? */
*((unsigned long*)(reinterpret_cast< uintptr_t >( stack_top)- 8)) = ss_sp;
/* Todo: place shadow stack info in 64byte gap */
*((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 8)) = (unsigned long) ss_base;
*((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 16)) = ss_size;
#endif
const fcontext_t fctx = make_fcontext( stack_top, size, & fiber_entry< Record >);
BOOST_ASSERT( nullptr != fctx);
// transfer control structure to context-stack
Expand All @@ -204,14 +259,6 @@ class fiber {
friend detail::transfer_t
detail::fiber_ontop( detail::transfer_t);

template< typename StackAlloc, typename Fn >
friend fiber
callcc( std::allocator_arg_t, StackAlloc &&, Fn &&);

template< typename StackAlloc, typename Fn >
friend fiber
callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&);

detail::fcontext_t fctx_{ nullptr };

fiber( detail::fcontext_t fctx) noexcept :
Expand Down
8 changes: 0 additions & 8 deletions include/boost/context/fiber_ucontext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,14 +397,6 @@ class BOOST_CONTEXT_DECL fiber {
template< typename Ctx, typename StackAlloc, typename Fn >
friend detail::fiber_activation_record * detail::create_fiber2( preallocated, StackAlloc &&, Fn &&);

template< typename StackAlloc, typename Fn >
friend fiber
callcc( std::allocator_arg_t, StackAlloc &&, Fn &&);

template< typename StackAlloc, typename Fn >
friend fiber
callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&);

detail::fiber_activation_record * ptr_{ nullptr };

fiber( detail::fiber_activation_record * ptr) noexcept :
Expand Down
8 changes: 0 additions & 8 deletions include/boost/context/fiber_winfib.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,14 +290,6 @@ class BOOST_CONTEXT_DECL fiber {
template< typename Ctx, typename StackAlloc, typename Fn >
friend detail::fiber_activation_record * detail::create_fiber2( preallocated, StackAlloc &&, Fn &&);

template< typename StackAlloc, typename Fn >
friend fiber
callcc( std::allocator_arg_t, StackAlloc &&, Fn &&);

template< typename StackAlloc, typename Fn >
friend fiber
callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&);

detail::fiber_activation_record * ptr_{ nullptr };

fiber( detail::fiber_activation_record * ptr) noexcept :
Expand Down
44 changes: 27 additions & 17 deletions src/asm/jump_i386_sysv_elf_gas.S
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
* ---------------------------------------------------------------------------------- *
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | *
* ---------------------------------------------------------------------------------- *
* | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | hidden | *
* | fc_mxcsr|fc_x87_cw| guard | EDI | ESI | EBX | EBP | EIP | *
* ---------------------------------------------------------------------------------- *
* ---------------------------------------------------------------------------------- *
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
* ---------------------------------------------------------------------------------- *
* | 0x20 | 0x24 | | *
* | 0x20 | 0x24 | 0x28 | | *
* ---------------------------------------------------------------------------------- *
* | to | data | | *
* | hidden | to | data | | *
* ---------------------------------------------------------------------------------- *
* *
****************************************************************************************/
Expand All @@ -30,50 +30,60 @@
.align 2
.type jump_fcontext,@function
jump_fcontext:
leal -0x18(%esp), %esp /* prepare stack */
leal -0x1c(%esp), %esp /* prepare stack */

#if !defined(BOOST_USE_TSX)
stmxcsr (%esp) /* save MMX control- and status-word */
fnstcw 0x4(%esp) /* save x87 control-word */
#endif

movl %edi, 0x8(%esp) /* save EDI */
movl %esi, 0xc(%esp) /* save ESI */
movl %ebx, 0x10(%esp) /* save EBX */
movl %ebp, 0x14(%esp) /* save EBP */
#if defined(BOOST_CONTEXT_TLS_STACK_PROTECTOR)
movl %gs:0x14, %ecx /* read stack guard from TLS record */
movl %ecx, 0x8(%esp) /* save stack guard */
#endif

movl %edi, 0xc(%esp) /* save EDI */
movl %esi, 0x10(%esp) /* save ESI */
movl %ebx, 0x14(%esp) /* save EBX */
movl %ebp, 0x18(%esp) /* save EBP */

/* store ESP (pointing to context-data) in ECX */
movl %esp, %ecx

/* first arg of jump_fcontext() == fcontext to jump to */
movl 0x20(%esp), %eax
movl 0x24(%esp), %eax

/* second arg of jump_fcontext() == data to be transferred */
movl 0x24(%esp), %edx
movl 0x28(%esp), %edx

/* restore ESP (pointing to context-data) from EAX */
movl %eax, %esp

/* address of returned transport_t */
movl 0x1c(%esp), %eax
movl 0x20(%esp), %eax
/* return parent fcontext_t */
movl %ecx, (%eax)
/* return data */
movl %edx, 0x4(%eax)

movl 0x18(%esp), %ecx /* restore EIP */
movl 0x1c(%esp), %ecx /* restore EIP */

#if !defined(BOOST_USE_TSX)
ldmxcsr (%esp) /* restore MMX control- and status-word */
fldcw 0x4(%esp) /* restore x87 control-word */
#endif

movl 0x8(%esp), %edi /* restore EDI */
movl 0xc(%esp), %esi /* restore ESI */
movl 0x10(%esp), %ebx /* restore EBX */
movl 0x14(%esp), %ebp /* restore EBP */
#if defined(BOOST_CONTEXT_TLS_STACK_PROTECTOR)
movl 0x8(%esp), %edx /* load stack guard */
movl %edx, %gs:0x14 /* restore stack guard to TLS record */
#endif

movl 0xc(%esp), %edi /* restore EDI */
movl 0x10(%esp), %esi /* restore ESI */
movl 0x14(%esp), %ebx /* restore EBX */
movl 0x18(%esp), %ebp /* restore EBP */

leal 0x20(%esp), %esp /* prepare stack */
leal 0x24(%esp), %esp /* prepare stack */

/* jump to context */
jmp *%ecx
Expand Down
Loading